<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
		<id>https://rn-wissen.de/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=-tomas-</id>
		<title>RN-Wissen.de - Benutzerbeiträge [de]</title>
		<link rel="self" type="application/atom+xml" href="https://rn-wissen.de/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=-tomas-"/>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Spezial:Beitr%C3%A4ge/-tomas-"/>
		<updated>2026-04-12T07:52:23Z</updated>
		<subtitle>Benutzerbeiträge</subtitle>
		<generator>MediaWiki 1.25.1</generator>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=V-USB:_Ein_Firmware_USB-Treiber_f%C3%BCr_AVR&amp;diff=16628</id>
		<title>V-USB: Ein Firmware USB-Treiber für AVR</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=V-USB:_Ein_Firmware_USB-Treiber_f%C3%BCr_AVR&amp;diff=16628"/>
				<updated>2010-05-13T17:58:48Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: Stand ausreichend für Kategorie:&amp;quot;Quellcode C&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mit [http://http://www.obdev.at/products/vusb/index.html V-USB] von Objective Development ist es ohne weitere Hardware möglich, mit einem AVR Mikrocontroller ein USB-Gerät aufzubauen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Hardware Voraussetzungen des AVRs ==&lt;br /&gt;
=== Spannungspegel-Problem ===&lt;br /&gt;
Da der AVR mit anderen Spannungen als die des USB-Standards arbeitet, müssen diese Angepasst werden. Der Computer und der AVR-Mikrocontroller haben beide bestimmte Anforderungen an die Spannung für einen High- bzw. Low-Pegel. Da wir die Computerseite nicht verändern können, müssen die Anpassungen am AVR stattfinden. Vom PC wird ein Low-Pegel als 0V, ein High-Pegel als 3,3V übertragen. Jedoch ist dies das kleinere Problem, da die AVR-Mikrocontroller selbst bei 5V diesen Pegel sicher als High erkennen.&lt;br /&gt;
Die andere Richtung ist ein größeres Problem. Der Computer erwartet als Low-Pegel eine Spannung von 0V - 0,8V und 2V - 3,6V als High. Um diese Voraussetzungen zu erfüllen, muss entweder der AVR mit einer Spannung von 2V - 3,6V betreiben werden oder die Pegel müssen an D+ und D- des AVRs angepasst werden.&lt;br /&gt;
&lt;br /&gt;
==== Lösung A: Verringern der Betriebspannung des AVRs ====&lt;br /&gt;
Für diese Lösung wird die Betriebsspannung des AVRs auf 3,3V - 3,6V herunter gesetzt.&lt;br /&gt;
Dies ist mit einem 3,3V-Spannungsregler (z.B. LE33CZ, LT1761ES5-3.3) möglich.&lt;br /&gt;
[[Bild:voltage-regulator.gif|thumb|Spannungsanpassung mit Dioden]]&lt;br /&gt;
&lt;br /&gt;
'''Vorteile'''&lt;br /&gt;
* Saubere Lösung. Schnelle Übergänge auf D+ und D-&lt;br /&gt;
* Gute Störfestigkeit am Signaleingang&lt;br /&gt;
* Stromverbrauch des µC ist bei 3,3 V geringer als bei 5 V&lt;br /&gt;
&lt;br /&gt;
'''Nachteile'''&lt;br /&gt;
* 3,3V-Spannungsregler sind oft teuer und schwer zu beschaffen&lt;br /&gt;
* Viele ältere AVRs funktionieren bei 3,3 V nicht sicher beim geforderten Takt (12 MHz). Auch bei neueren sind bei 3.3 V sind kaum mehr als 13 MHz garantiert. &lt;br /&gt;
* Ruhestrom für den Spannungsregler (Forderung nach sparsamem Regler)&lt;br /&gt;
&lt;br /&gt;
Ebenso ist es möglich, die Spannung mit Gleichrichterdioden zu reduzieren.&lt;br /&gt;
Hierfür werden einfach zwei oder drei Gleichrichterdioden in Reihe vor VCC des AVRs geschaltet.&lt;br /&gt;
[[Bild:voltage-reduction-with-diodes.gif|thumb|Pegelanpassung mit Zenerdioden]]&lt;br /&gt;
&lt;br /&gt;
'''Zusätzliche Vorteile'''&lt;br /&gt;
* Niedrige kosten / Bauteile sind leicht zu bekommen.&lt;br /&gt;
* kein Ruhestrom --&amp;gt; USB-Konformer Standby-Modus&lt;br /&gt;
&lt;br /&gt;
'''Zusätzliche Nachteile'''&lt;br /&gt;
* Keine Spannungsregelung --&amp;gt; Spannung schwankt, das Geschwindigkeitsprobelm wird dadurch noch größer. Eventuell zu viel Spannung im Leerlauf.  &lt;br /&gt;
* Die unregulierte Spannung ist problematisch für Analoge Schaltungen (ADC,...)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lösung B: Anpassen der Spannung an D+ und D- ====&lt;br /&gt;
Ebenso kann man die Spannung an den Datenleitungen (D+ und D-) mit Zenerdioden verringern. Es werden 3,6V Low-Power Zenerdioden (wie 1N4148, 500mW oder weniger) empfohlen, da diese eine geringe Kapazität haben und somit weniger Störungen auf den Datenleitungen verursachen.&lt;br /&gt;
[[Bild:level-conversion-with-zener.gif|thumb|Pegelanpassung mit Zenerdioden]]&lt;br /&gt;
&lt;br /&gt;
Wenn diese Lösung verwendet wird, stellen sie vor Benutzung sicher, dass die Spannungspegel mit den geforderten Pegeln(LOW:0V-0,8V HIGH:2V-3,6V) übereinstimmen.&lt;br /&gt;
&lt;br /&gt;
'''Vorteile'''&lt;br /&gt;
* Niedrige Kosten&lt;br /&gt;
* Leicht zu bekommen&lt;br /&gt;
* Ganze Schaltung kann mit 5V betrieben werden --&amp;gt; Hohe Taktraten möglich&lt;br /&gt;
* zusätzlicher Schutz vor Überspannungen / ESD&lt;br /&gt;
&lt;br /&gt;
'''Nachteile'''&lt;br /&gt;
* Keine saubere Lösung: Es muss ein Kompromiss zwischen allen Möglichkeiten gefunden werden.&lt;br /&gt;
* Zener-Dioden kommen mit einem breiten Spektrum an Merkmalen. Deshalb könnten die Ergebnisse nicht reproduzierbar sein.&lt;br /&gt;
* Hohe Ströme beim Senden von High-Leveln&lt;br /&gt;
* Nicht viel Reserve beim Empfangen&lt;br /&gt;
&lt;br /&gt;
=== Allgemeine Anmerkungen zur Hardware ===&lt;br /&gt;
Es wird bei jeder der oben genannten Schaltungen ein PullUp-Widerstand (1,5k - 10k) von D- nach Vcc benötigt. Ebenso muss zwischen AVR und Die Datenleitungen ein Widerstand von je 68 Ohm.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Welche Taktraten können verwendet werden? ===&lt;br /&gt;
Momentan unterstützt V-USB Taktraten von 12 MHz, 12.8 MHz, 15 MHz, 16 MHz, 16.5 MHz, 18 MHz und 20 MHz. Diese Taktraten sind präzise, dies bedeutet, dass ein Quarz mit 11.9 MHz nicht funktionieren würde. Nur bei den 16.5 und 12.8 MHz Varianten ist eine Toleranz von 1%.16.5 MHz kann z.B. mit dem internen RC Oszillator von AVRs wie dem ATTiny25/45/85 oder dem ATTiny26 erreicht werden.&lt;br /&gt;
'''Entscheidungshilfe'''&lt;br /&gt;
* Möchten sie den internen RC Oszillator benutzen? Dann nehmen sie die 16.5MHz-Variante.&lt;br /&gt;
* Ist sehr wenig Speicher vorhanden? Verwenden sie am besten die 16MHz oder 20Mhz Variante.&lt;br /&gt;
* Wird der AVR mit einer niedrigen Spannung betrieben? Dann verwenden sie die 12MHz-Variante.&lt;br /&gt;
* Wollen sie CRC-Prüfsummen verwenden? Benutzen sie die 18Mhz-Variante.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anschluss an den AVR ===&lt;br /&gt;
Die Datenleitung D+ muss über den 68 Ohm-Widerstand mit dem Port INT0 des AVRs verbunden werden. D- kann an einen beliebigen Port. Diese Ports müssen dann in der &amp;quot;usbconfig.h&amp;quot; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Software des AVR: Die Firmware ==&lt;br /&gt;
Als erstes muss man sich die neueste Version von V-USB von folgender Seite herunterladen: [http://www.obdev.at/products/vusb/download.html]&lt;br /&gt;
Nachdem sie das Archiv heruntergeladen haben, entpacken sie es und kopieren sie den Ordner &amp;quot;usbdrv&amp;quot; in ihr Projektverzeichnis. Kopieren bzw. erstellen sie das Makefile und passen darin die Taktrate und den verwendeten Controller an. Ebenso wird eine &amp;quot;usbconfig.h&amp;quot; benötigt, diese kann z.B. aus dem tests-Ordner kopiert werden. In dieser müssen nun die Punkte USB_CFG_IOPORTNAME, USB_CFG_DMINUS_BIT und USB_CFG_DPLUS_BIT angepasst werden.&lt;br /&gt;
&lt;br /&gt;
In der main.c müssen immer als erstes folgende Dateien importiert werden:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt; //IO-Zugriff: Braucht man immer&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt; // V-USB braucht interrupts&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/wdt.h&amp;gt; //Watchdog: Sollte immer aktiv sein.&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;usbdrv.h&amp;quot; //Der USB-Treiber&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt; //Wird später für Timing etc. benötigt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Gerät mit selbst geschriebenem PC-Treiber (kein HID) ===&lt;br /&gt;
Der Code für ein eigenes Gerät wird hier am Beispiel eines Gerätes veranschaulicht, bei dem sich über USB der PWM-Kanal steuern lässt. Damit kann man zum Beispiel eine LED dimmen.&lt;br /&gt;
&lt;br /&gt;
Nach den Includes komm nun die Funktion zum Verarbeiten eingehender Daten.&lt;br /&gt;
Das erste Empfangene Byte, also der Befehl wird mit rq-&amp;gt;bRequest abgefragt. In diesem Fall ist dieser entweder 0 oder 1. 0 bedeutet den PWM-Wert zu setzen, 1 bedeutet den Status (PWM-Wert) zurückzusenden.&lt;br /&gt;
Die einzelnen Parameter des Aufrufs können mit rq-&amp;gt;wValue.bytes[id] abgefragt werden. Bei Befehl 0 wird in rq-&amp;gt;wValue.bytes[0] der neue PWM-Wert übertragen.&lt;br /&gt;
Durch setzen von replyBuf kann festgelegt werden, welche Daten zurückgesendet werden sollen. Dabei muss immer replyBuf[id] verwedet werden. Bei Befehl 1 wird mit replyBuf[0] = OCR1A der aktuelle PWM-Wert zurückgesendet.&lt;br /&gt;
Mit return 2 wird dann noch mitgeteilt, dass überhaupt was zurückgesendet werden soll. Mit return 0 wird nichts zurückgesendet. Das ist in diesem Fall der Fall, wenn ein ungültiger Befehl übermittelt wurde.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
USB_PUBLIC uchar usbFunctionSetup(uchar data[8])&lt;br /&gt;
{&lt;br /&gt;
usbRequest_t    *rq = (void *)data;&lt;br /&gt;
static uchar    replyBuf[2];&lt;br /&gt;
&lt;br /&gt;
    usbMsgPtr = replyBuf;&lt;br /&gt;
    if(rq-&amp;gt;bRequest == 0){&lt;br /&gt;
	replyBuf[0] = rq-&amp;gt;wValue.bytes[0];&lt;br /&gt;
        replyBuf[1] = 0x0;&lt;br /&gt;
&lt;br /&gt;
	OCR1A=rq-&amp;gt;wValue.bytes[0];&lt;br /&gt;
	eeprom_write_byte(0,OCR1A);&lt;br /&gt;
	return 2;&lt;br /&gt;
    }&lt;br /&gt;
    else if (rq-&amp;gt;bRequest == 1) {&lt;br /&gt;
	replyBuf[0] = OCR1A;&lt;br /&gt;
        replyBuf[1] = 0x0;&lt;br /&gt;
	return 2;&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Als nächstes komm void main:&lt;br /&gt;
Hier schalten wir als erstes den Watchdog-Timer ein. Danach setzen wir alle Ports und DDR so wie wir sie brauchen. Es ist nur darauf zu achten, dass INT0 ein Eingang ist. Bei einem Atmega8 ist dies PORTD.2. Für unser Beispiel setzen wir TCCR1A als 8_bit PWM-Timer. Danach laden wir den letzten PWM-Wert aus dem EEPROM (Wird immer beim Ändern gespeichert). Nun trennen wir die Verbindung für &amp;gt; 500ms, dies wird benötigt, um nach einen Neustart des Mikrocontrollers (z.B. durch Watchdog) dem PC mitzuteilen, dass er neugestartet ist. Danach Verbinden wir uns wieder mit usbDeviceConnect(). Nun muss die Verbindung mit usbInit() initialisiert werden ud die Interrupts mit sei() eingeschaltet werden. Nun muss nur noch im Mainloop der Watchdog zurückgesetzt werden und usbPoll() aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
uchar   i;&lt;br /&gt;
&lt;br /&gt;
    wdt_enable(WDTO_1S);&lt;br /&gt;
    DDRD = ~(1 &amp;lt;&amp;lt; 2);   /* all outputs except PD2 = INT0 */&lt;br /&gt;
    DDRB = 0xff;    /* output pins setzen */&lt;br /&gt;
    PORTD = 0;     /* USB-Verbindung */&lt;br /&gt;
    PORTB = 0;     /* output pins aus*/&lt;br /&gt;
&lt;br /&gt;
	TCCR1A = (1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1); // PWM, phase correct, 8 bit.&lt;br /&gt;
	TCCR1B =  (1&amp;lt;&amp;lt;CS11) |(1&amp;lt;&amp;lt;CS10); // set clock/prescaler 1/64 -&amp;gt; enable counter&lt;br /&gt;
	OCR1A=eeprom_read_byte(0);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* We fake an USB disconnect by pulling D+ and D- to 0 during reset. This is&lt;br /&gt;
 * necessary if we had a watchdog reset or brownout reset to notify the host&lt;br /&gt;
 * that it should re-enumerate the device. Otherwise the host's and device's&lt;br /&gt;
 * concept of the device-ID would be out of sync.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
    usbDeviceDisconnect();  /* enforce re-enumeration, do this while interrupts are disabled! */&lt;br /&gt;
    i = 0;&lt;br /&gt;
    while(--i){         /* fake USB disconnect for &amp;gt; 500 ms */&lt;br /&gt;
        wdt_reset();&lt;br /&gt;
        _delay_ms(2);&lt;br /&gt;
    }&lt;br /&gt;
    usbDeviceConnect();&lt;br /&gt;
    usbInit();&lt;br /&gt;
    sei();&lt;br /&gt;
    for(;;){    /* main event loop */&lt;br /&gt;
        wdt_reset();&lt;br /&gt;
        usbPoll();&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit ist die Firmware fertig. Hier noch ein Makefile für avr-gcc: (Chip: Mega8, Clock: 12MHz, Programmer: Ponyser)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Name: Makefile&lt;br /&gt;
# Project: edited PowerSwitch&lt;br /&gt;
# Author: &lt;br /&gt;
# Creation Date: &lt;br /&gt;
# Tabsize: 4&lt;br /&gt;
# Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH&lt;br /&gt;
# License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)&lt;br /&gt;
# This Revision: $Id: Makefile 277 2007-03-20 10:53:33Z cs $&lt;br /&gt;
&lt;br /&gt;
DEVICE = atmega8&lt;br /&gt;
AVRDUDE = avrdude -c ponyser -P /dev/ttyS0 -p $(DEVICE)&lt;br /&gt;
# Choose your favorite programmer and interface above.&lt;br /&gt;
&lt;br /&gt;
COMPILE = avr-gcc -Wall -Os -Iusbdrv -I. -mmcu=$(DEVICE) #-DDEBUG_LEVEL=2&lt;br /&gt;
# NEVER compile the final product with debugging! Any debug output will&lt;br /&gt;
# distort timing so that the specs can't be met.&lt;br /&gt;
&lt;br /&gt;
OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o&lt;br /&gt;
&lt;br /&gt;
# symbolic targets:&lt;br /&gt;
all:	main.hex&lt;br /&gt;
&lt;br /&gt;
.c.o:&lt;br /&gt;
	$(COMPILE) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
.S.o:&lt;br /&gt;
	$(COMPILE) -x assembler-with-cpp -c $&amp;lt; -o $@&lt;br /&gt;
# &amp;quot;-x assembler-with-cpp&amp;quot; should not be necessary since this is the default&lt;br /&gt;
# file type for the .S (with capital S) extension. However, upper case&lt;br /&gt;
# characters are not always preserved on Windows. To ensure WinAVR&lt;br /&gt;
# compatibility define the file type manually.&lt;br /&gt;
&lt;br /&gt;
.c.s:&lt;br /&gt;
	$(COMPILE) -S $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
flash:	all&lt;br /&gt;
	$(AVRDUDE) -U flash:w:main.hex:i&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Fuse low byte:&lt;br /&gt;
# 0xef = 1 1 1 0   1 1 1 1&lt;br /&gt;
#        ^ ^ \+/   \--+--/&lt;br /&gt;
#        | |  |       +------- CKSEL 3..0 (clock selection -&amp;gt; crystal @ 12 MHz)&lt;br /&gt;
#        | |  +--------------- SUT 1..0 (BOD enabled, fast rising power)&lt;br /&gt;
#        | +------------------ CKOUT (clock output on CKOUT pin -&amp;gt; disabled)&lt;br /&gt;
#        +-------------------- CKDIV8 (divide clock by 8 -&amp;gt; don't divide)&lt;br /&gt;
#&lt;br /&gt;
# Fuse high byte:&lt;br /&gt;
# 0xdb = 1 1 0 1   1 0 1 1&lt;br /&gt;
#        ^ ^ ^ ^   \-+-/ ^&lt;br /&gt;
#        | | | |     |   +---- RSTDISBL (disable external reset -&amp;gt; enabled)&lt;br /&gt;
#        | | | |     +-------- BODLEVEL 2..0 (brownout trigger level -&amp;gt; 2.7V)&lt;br /&gt;
#        | | | +-------------- WDTON (watchdog timer always on -&amp;gt; disable)&lt;br /&gt;
#        | | +---------------- SPIEN (enable serial programming -&amp;gt; enabled)&lt;br /&gt;
#        | +------------------ EESAVE (preserve EEPROM on Chip Erase -&amp;gt; not preserved)&lt;br /&gt;
#        +-------------------- DWEN (debug wire enable)&lt;br /&gt;
#fuse_tiny2313:	# only needed for attiny2313&lt;br /&gt;
	# $(AVRDUDE) -U hfuse:w:0xdb:m -U lfuse:w:0xef:m&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -f main.hex main.lst main.obj main.cof main.list main.map main.eep.hex main.bin *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s&lt;br /&gt;
&lt;br /&gt;
# file targets:&lt;br /&gt;
main.bin:	$(OBJECTS)&lt;br /&gt;
	$(COMPILE) -o main.bin $(OBJECTS)&lt;br /&gt;
&lt;br /&gt;
main.hex:	main.bin&lt;br /&gt;
	rm -f main.hex main.eep.hex&lt;br /&gt;
	avr-objcopy -j .text -j .data -O ihex main.bin main.hex&lt;br /&gt;
	./checksize main.bin&lt;br /&gt;
# do the checksize script as our last action to allow successful compilation&lt;br /&gt;
# on Windows with WinAVR where the Unix commands will fail.&lt;br /&gt;
&lt;br /&gt;
disasm:	main.bin&lt;br /&gt;
	avr-objdump -d main.bin&lt;br /&gt;
&lt;br /&gt;
cpp:&lt;br /&gt;
	$(COMPILE) -E main.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D+ ist in diesem Beispiel mit PD2 verbunden, D- mit PD0. Die LED hängt an PB1. (Schaltplan noch in Arbeit.)&lt;br /&gt;
[http://www.rn-wissen.de/index.php/V-USB:_Ein_Firmware_USB-Treiber_f%C3%BCr_AVR#PC-Software_bei_nicht_HID-Projekten_.28Treiber.29 Hier gehts zur PC-Seitigen Programmierung dieses Projekts]&lt;br /&gt;
&lt;br /&gt;
=== HID-Gerät (Tastatur/Maus/...) ===&lt;br /&gt;
[noch in Arbeit]&lt;br /&gt;
&lt;br /&gt;
== PC-Software bei nicht HID-Projekten (Treiber) ==&lt;br /&gt;
Wenn man nun ein Gerät mit einer Firmware hat, muss man als nächstes den Treiber für den PC schreiben. Dies werde ich nun am obigen Beispiel erläutern.&lt;br /&gt;
&lt;br /&gt;
Als erstes sollte man sich überlegen, was die Software können muss. In unserem Beispiel wären das:&lt;br /&gt;
* Den PWM-Wert setzen&lt;br /&gt;
* Befehl ein/aus&lt;br /&gt;
* aktuellen Wert auslesen&lt;br /&gt;
Die Befehle ein bzw. aus müssen dabei keine eigenen Befehle werden, da man dafür den Wert 255(an) bzw. 0(aus) senden kann.&lt;br /&gt;
&lt;br /&gt;
Um nun überhaupt einen USB-Treiber schreiben zu können, muss man sich als erstes &amp;quot;libusb&amp;quot; für c bzw. &amp;quot;libusb++&amp;quot; für c++ herunterladen. Unter Linux kann dies über den Paketmanager (Synaptic/Aptitude) geschehen. Unter Windows muss man sich die Bibliothek von folgender Seite herunterladen: [http://www.libusb.org/ http://www.libusb.org/]/[http://libusb.sourceforge.net/ http://libusb.sourceforge.net/]. Stellen sie sicher, dass wenn sie unter Windows arbeiten, die Win32-Version herunterladen.&lt;br /&gt;
&lt;br /&gt;
Als erstes muss man auch wieder einige Header importieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;  /* Brauchen wir für Ein-/ bzw. Ausgabe */&lt;br /&gt;
#include &amp;lt;string.h&amp;gt; /* Brauchen wir für die Auswertung von Parametern */&lt;br /&gt;
#include &amp;lt;usb.h&amp;gt;    /* Die Bibliothek - Liegt diese im Projektverzeichnis so muss #include &amp;quot;usb.h&amp;quot; verwendet werden*/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als nächstes müssen die VendorID und PID definiert werden. Diese können aus der Datei &amp;quot;USBID-License.txt&amp;quot; entnommen werden. WICHTIG: Zuerst ganz durchlesen um die Richtige auszuwählen.&lt;br /&gt;
Diese definiert man dann mit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#define USBDEV_SHARED_VENDOR    0x16C0  /* VOTI */&lt;br /&gt;
#define USBDEV_SHARED_PRODUCT   0x05DC  /* Obdev's free shared PID */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In diesem Beispiel wurde die VendorID/PID so gewählt, dass es keine Klasse hat, also ein eigenes Gerät ist.&lt;br /&gt;
&lt;br /&gt;
Danach sollte man der Übersichtlichkeit halber, die einzelnen Befehle (welche einzelne Bytes sind) definieren. Für unser Beispiel heißt das:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#define CMD_SETPWM  0&lt;br /&gt;
#define CMD_GETSTATUS   1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da dieses Projekt eine Konsolen-basierte Anwendung ist, ist es immer wichtig eine Hilfe mit den befehlen einzubauen. Nennen wir diese Funktion einfach usage: (Es kann selbstverständlich auch auf stdout ausgegeben werden.)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static void usage(char *name)&lt;br /&gt;
{&lt;br /&gt;
    fprintf(stderr, &amp;quot;usage:\n&amp;quot;);&lt;br /&gt;
    fprintf(stderr, &amp;quot;  %s on\n&amp;quot;, name);&lt;br /&gt;
    fprintf(stderr, &amp;quot;  %s off\n&amp;quot;, name);&lt;br /&gt;
    fprintf(stderr, &amp;quot;  %s pwm [0-255]\n&amp;quot;, name);&lt;br /&gt;
    fprintf(stderr, &amp;quot;  %s status\n&amp;quot;, name);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als nächstes brauchen wir Funktionen zum Öffnen und Kommunizieren mit dem Gerät. Diese wurden aus dem Beispiel &amp;quot;PowerSwitch&amp;quot; von obdev kopiert:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static int  usbGetStringAscii(usb_dev_handle *dev, int index, int langid, char *buf, int buflen)&lt;br /&gt;
{&lt;br /&gt;
char    buffer[256];&lt;br /&gt;
int     rval, i;&lt;br /&gt;
&lt;br /&gt;
    if((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING &amp;lt;&amp;lt; 8) + index, langid, buffer, sizeof(buffer), 1000)) &amp;lt; 0)&lt;br /&gt;
        return rval;&lt;br /&gt;
    if(buffer[1] != USB_DT_STRING)&lt;br /&gt;
        return 0;&lt;br /&gt;
    if((unsigned char)buffer[0] &amp;lt; rval)&lt;br /&gt;
        rval = (unsigned char)buffer[0];&lt;br /&gt;
    rval /= 2;&lt;br /&gt;
    /* lossy conversion to ISO Latin1 */&lt;br /&gt;
    for(i=1;i&amp;lt;rval;i++){&lt;br /&gt;
        if(i &amp;gt; buflen)  /* destination buffer overflow */&lt;br /&gt;
            break;&lt;br /&gt;
        buf[i-1] = buffer[2 * i];&lt;br /&gt;
        if(buffer[2 * i + 1] != 0)  /* outside of ISO Latin1 range */&lt;br /&gt;
            buf[i-1] = '?';&lt;br /&gt;
    }&lt;br /&gt;
    buf[i-1] = 0;&lt;br /&gt;
    return i-1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define USB_ERROR_NOTFOUND  1&lt;br /&gt;
#define USB_ERROR_ACCESS    2&lt;br /&gt;
#define USB_ERROR_IO        3&lt;br /&gt;
&lt;br /&gt;
static int usbOpenDevice(usb_dev_handle **device, int vendor, char *vendorName, int product, char *productName)&lt;br /&gt;
{&lt;br /&gt;
struct usb_bus      *bus;&lt;br /&gt;
struct usb_device   *dev;&lt;br /&gt;
usb_dev_handle      *handle = NULL;&lt;br /&gt;
int                 errorCode = USB_ERROR_NOTFOUND;&lt;br /&gt;
static int          didUsbInit = 0;&lt;br /&gt;
&lt;br /&gt;
    if(!didUsbInit){&lt;br /&gt;
        didUsbInit = 1;&lt;br /&gt;
        usb_init();&lt;br /&gt;
    }&lt;br /&gt;
    usb_find_busses();&lt;br /&gt;
    usb_find_devices();&lt;br /&gt;
    for(bus=usb_get_busses(); bus; bus=bus-&amp;gt;next){&lt;br /&gt;
        for(dev=bus-&amp;gt;devices; dev; dev=dev-&amp;gt;next){&lt;br /&gt;
            if(dev-&amp;gt;descriptor.idVendor == vendor &amp;amp;&amp;amp; dev-&amp;gt;descriptor.idProduct == product){&lt;br /&gt;
                char    string[256];&lt;br /&gt;
                int     len;&lt;br /&gt;
                handle = usb_open(dev); /* we need to open the device in order to query strings */&lt;br /&gt;
                if(!handle){&lt;br /&gt;
                    errorCode = USB_ERROR_ACCESS;&lt;br /&gt;
                    fprintf(stderr, &amp;quot;Warning: cannot open USB device: %s\n&amp;quot;, usb_strerror());&lt;br /&gt;
                    continue;&lt;br /&gt;
                }&lt;br /&gt;
                if(vendorName == NULL &amp;amp;&amp;amp; productName == NULL){  /* name does not matter */&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                /* now check whether the names match: */&lt;br /&gt;
                len = usbGetStringAscii(handle, dev-&amp;gt;descriptor.iManufacturer, 0x0409, string, sizeof(string));&lt;br /&gt;
                if(len &amp;lt; 0){&lt;br /&gt;
                    errorCode = USB_ERROR_IO;&lt;br /&gt;
                    fprintf(stderr, &amp;quot;Warning: cannot query manufacturer for device: %s\n&amp;quot;, usb_strerror());&lt;br /&gt;
                }else{&lt;br /&gt;
                    errorCode = USB_ERROR_NOTFOUND;&lt;br /&gt;
                    /* fprintf(stderr, &amp;quot;seen device from vendor -&amp;gt;%s&amp;lt;-\n&amp;quot;, string); */&lt;br /&gt;
                    if(strcmp(string, vendorName) == 0){&lt;br /&gt;
                        len = usbGetStringAscii(handle, dev-&amp;gt;descriptor.iProduct, 0x0409, string, sizeof(string));&lt;br /&gt;
                        if(len &amp;lt; 0){&lt;br /&gt;
                            errorCode = USB_ERROR_IO;&lt;br /&gt;
                            fprintf(stderr, &amp;quot;Warning: cannot query product for device: %s\n&amp;quot;, usb_strerror());&lt;br /&gt;
                        }else{&lt;br /&gt;
                            errorCode = USB_ERROR_NOTFOUND;&lt;br /&gt;
                            /* fprintf(stderr, &amp;quot;seen product -&amp;gt;%s&amp;lt;-\n&amp;quot;, string); */&lt;br /&gt;
                            if(strcmp(string, productName) == 0)&lt;br /&gt;
                                break;&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                usb_close(handle);&lt;br /&gt;
                handle = NULL;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        if(handle)&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
    if(handle != NULL){&lt;br /&gt;
        errorCode = 0;&lt;br /&gt;
        *device = handle;&lt;br /&gt;
    }&lt;br /&gt;
    return errorCode;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als letztes kommt nun void main. In main muss als erstes ein Handle für das USB-Gerät definiert werden. Danach brauchen wir noch einen Buffer für Dinge, die vom Gerät kommen und dann noch einen Zähler für empfangene Bytes.&lt;br /&gt;
Danach prüfen wir, ob der Programmaufruf korrekt ist. (Parameterzahl):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
usb_dev_handle      *handle = NULL;&lt;br /&gt;
unsigned char       buffer[8];&lt;br /&gt;
int                 nBytes;&lt;br /&gt;
&lt;br /&gt;
    if(argc &amp;lt; 2){&lt;br /&gt;
        usage(argv[0]);&lt;br /&gt;
        exit(1);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Danach muss libusb initialisiert werden dann das Gerät mit der obigen Funktion geöffnet werden. Dabei musst du natürlich Email/Homepage durch deine Homepage bzw. E-Mail ersetzen. Ebenso muss der Name, in diesem Fall PWMControl angepasst werden. Die beiden Werte wurden vorher in der Datei &amp;quot;usbconfig.h&amp;quot; festgelegt. Lesen sie dazu jedoch die Datei &amp;quot;USBID-License.txt&amp;quot;. Diese Werte können natürlich auch als Defines gesetzt werden.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
usb_init();&lt;br /&gt;
    if(usbOpenDevice(&amp;amp;handle, USBDEV_SHARED_VENDOR, &amp;quot;EMail/Homepage&amp;quot;, USBDEV_SHARED_PRODUCT, &amp;quot;PWMControl&amp;quot;) != 0){&lt;br /&gt;
        fprintf(stderr, &amp;quot;Could not find USB device \&amp;quot;PWMControl\&amp;quot; with vid=0x%x pid=0x%x\n&amp;quot;, USBDEV_SHARED_VENDOR, USBDEV_SHARED_PRODUCT);&lt;br /&gt;
        exit(1);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Als nächstes kommt unser eigentlicher Programmcode. In diesem wird zuerst überprüft ob on/off/pwm oder status übergeben wurde. Bei PWM wird zusätzlich geprüft, ob noch der PWM-Wert mit angegeben wurde. Mit dem befehl '' nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, [Befehl], [Param1], [Param2], (char *)buffer, sizeof(buffer), 5000);'' kann man nun Daten an das Gerät schicken. Sendet das Gerät Daten zurück, so sind diese in buffer abrufbar. Ebenfalls wird noch überprüft, ob ein fehler auftrat. Dies erfolgt mit ''if(nBytes &amp;lt; 0)''.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if(strcmp(argv[1], &amp;quot;pwm&amp;quot;) == 0){&lt;br /&gt;
		   if(argc &amp;lt; 3){&lt;br /&gt;
            		usage(argv[0]);&lt;br /&gt;
            		exit(1);&lt;br /&gt;
        	   }&lt;br /&gt;
            nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_SETPWM, atoi(argv[2]), 0, (char *)buffer, sizeof(buffer), 5000);&lt;br /&gt;
        }else if(strcmp(argv[1], &amp;quot;off&amp;quot;) == 0){&lt;br /&gt;
            nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_SETPWM, 0, 255, (char *)buffer, sizeof(buffer), 5000);&lt;br /&gt;
	}else if(strcmp(argv[1], &amp;quot;on&amp;quot;) == 0){&lt;br /&gt;
            nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_SETPWM, 255, 255, (char *)buffer, sizeof(buffer), 5000);       &lt;br /&gt;
	}else if(strcmp(argv[1], &amp;quot;status&amp;quot;) == 0){&lt;br /&gt;
            nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_GETSTATUS, 0, 0, (char *)buffer, sizeof(buffer), 5000);       	&lt;br /&gt;
	&lt;br /&gt;
 	if(nBytes &amp;lt; 2){&lt;br /&gt;
            if(nBytes &amp;lt; 0)&lt;br /&gt;
                fprintf(stderr, &amp;quot;USB error: %s\n&amp;quot;, usb_strerror());&lt;br /&gt;
            fprintf(stderr, &amp;quot;only %d bytes status received\n&amp;quot;, nBytes);&lt;br /&gt;
            exit(1);&lt;br /&gt;
        }&lt;br /&gt;
	printf(&amp;quot;%i\n&amp;quot;, buffer[0]);&lt;br /&gt;
&lt;br /&gt;
	}else{&lt;br /&gt;
            nBytes = 0;&lt;br /&gt;
            usage(argv[0]);&lt;br /&gt;
            exit(1);&lt;br /&gt;
        }&lt;br /&gt;
        if(nBytes &amp;lt; 0)&lt;br /&gt;
            fprintf(stderr, &amp;quot;USB error: %s\n&amp;quot;, usb_strerror());&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Letztendlich muss man das USB-gerät nur noch schließen und den void main beenden.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    usb_close(handle); //USB-Gerät schließen&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellen ==&lt;br /&gt;
* [http://www.obdev.at/products/vusb/index.html V-USB Homepage by Objective Development]&lt;br /&gt;
* [http://vusb.wikidot.com/ V-USB Wiki] &lt;br /&gt;
* Alle drei Schaltpläne wurden aus der Wiki von V-USB entnommen.&lt;br /&gt;
[[Kategorie:Microcontroller]]&lt;br /&gt;
{{Ausbauwunsch|Das Thema HID-Geräte fehlt noch komplett.}}&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode C]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Diskussion:V-USB:_Ein_Firmware_USB-Treiber_f%C3%BCr_AVR&amp;diff=16627</id>
		<title>Diskussion:V-USB: Ein Firmware USB-Treiber für AVR</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Diskussion:V-USB:_Ein_Firmware_USB-Treiber_f%C3%BCr_AVR&amp;diff=16627"/>
				<updated>2010-05-13T17:53:48Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;!!!großartiger Artikel!!!&lt;br /&gt;
Ich nutze seit Jahren V-USB und bin über die geringe Verbreitung verwundert.&lt;br /&gt;
Ich hoffe der Artikel senkt die Hürden für Einsteiger.&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Speicherverbrauch_bestimmen_mit_avr-gcc&amp;diff=14557</id>
		<title>Speicherverbrauch bestimmen mit avr-gcc</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Speicherverbrauch_bestimmen_mit_avr-gcc&amp;diff=14557"/>
				<updated>2009-02-10T21:48:52Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Flash- und statischer RAM-Verbrauch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Der Speicherverbrauch eines C-Programmes lässt sich unter verschiedenen Gesichtspunkten betrachten.&lt;br /&gt;
;Ort der Datenablage:&lt;br /&gt;
* SRAM&lt;br /&gt;
* Flash&lt;br /&gt;
* EEPROM &lt;br /&gt;
&lt;br /&gt;
;Art der Daten:&lt;br /&gt;
* Programmcode&lt;br /&gt;
* Daten&lt;br /&gt;
** veränderlich/unveränderlich&lt;br /&gt;
** persistent oder flüchtig&lt;br /&gt;
&lt;br /&gt;
;Speicherklasse der Daten&lt;br /&gt;
* Statischer Speicherverbrauch&lt;br /&gt;
* Dynamischer Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
Der Ort der Datenablage wird bestimmt von der Art der Daten. Daten, die nicht verändert werden dürfen oder nicht verändert werden müssen, wie der Programm-Code oder konstante Strings, wie sie oft zur Ausgabe verwendet werden, können im Flash gespeichert werden. Konstanten/Tabellen kann man auch im EEPROM speichern, wenn der Platz im Flash knapp ist. Der Programmcode selbst muss bei [[AVR]] im Flash liegen. Man kann den Code zwar auch ins RAM oder ins EEPROM legen, aber von dort nicht ausführen.&lt;br /&gt;
&lt;br /&gt;
Daten, die zur Laufzeit verändert werden müssen, wird man im SRAM speichern, wenn sie einen Reset bzw. ein Ausschalten des Controllers nicht überleben müssen. Sollen die Daten auch ohne Strom erhalten bleiben, muss man den EEPROM oder den Flash als Ablageort wählen. Dabei ist das Speichern von Daten im Flash zur Laufzeit sehr aufwändig, weil man einen [[Bootloader]] für ihre Änderung anstrengen muss. Andererseits kann wesentlich schneller auf das Flash zugegriffen werden als auf den EEPROM-Speicher.&lt;br /&gt;
&lt;br /&gt;
Die Art der Daten ergibt sich aus dem Programm und den zu lösenden Aufgaben,&lt;br /&gt;
gleiches gilt für die Speicherklassen. &lt;br /&gt;
&lt;br /&gt;
Die Ablageorte der statische Daten sind bereits zur Compilezeit &lt;br /&gt;
bekannt. Hierzu gehören globale und statische Variablen. Dementsprechend ist auch schon zur Compile- bzw. Linkzeit bekannt, wieviel Speicher diese Daten belegen. &lt;br /&gt;
&lt;br /&gt;
Speicherorte und Platzverbrauch dynamischer Variablen sind dem Compiler nicht bekannt. Sie ergeben sich erst zur Laufzeit durch das Allokieren von Speicher mit &amp;lt;tt&amp;gt;malloc&amp;lt;/tt&amp;gt;, oder durch den Aufbau eines Stapels, auf dem lokale Variablen gesichert werden, während eine Funktion aufgerufen wird. Je nach Verschachtelungstiefe der Funktionsaufrufe &amp;amp;mdash; dazu gehören auch [[Interrupt]] Service Routinen, die prinzipiell jederzeit aufgerufen werden können &amp;amp;mdash; wird dafür auch unterschiedlich viel Speicher benötigt.&lt;br /&gt;
&lt;br /&gt;
=Nutzung des SRAM durch avr-gcc=&lt;br /&gt;
[[Bild:Avr-ram.png|right|thumb|RAM-Layout für ein AVR mit 1kByte SRAM]]&lt;br /&gt;
[[avr-gcc]] legt die Daten in [[avr-gcc/Interna#Sections|Sections]] an.&lt;br /&gt;
Nach aufsteigenden Speicheradressen sortiert sind diese:&lt;br /&gt;
;&amp;lt;tt&amp;gt;.data&amp;lt;/tt&amp;gt;: Statische und (modul-)globale, initialisierte Daten, denen man per Initializer einen Wert ungleich&amp;amp;nbsp;0 zuweist. Beginnt an der unteren SRAM-Adresse &amp;lt;tt&amp;gt;0x60&amp;lt;/tt&amp;gt; nach dem SFR-Bereich, je nach AVR-Derivat auch an anderer Adresse.&lt;br /&gt;
;&amp;lt;tt&amp;gt;.bss&amp;lt;/tt&amp;gt;: Statische und (modul-)globale, initialisierte Daten, die zu&amp;amp;nbsp;0 initialisiert sind bzw. keinen Initializer haben (und also auch zu&amp;amp;nbsp;0 initialisiert werden).&lt;br /&gt;
;&amp;lt;tt&amp;gt;.noinit&amp;lt;/tt&amp;gt;: Statische und (modul-)globale Daten, die nicht vom Startup-Code initialisiert werden und zum Beispiel einen [[Watchdog]]-Reset überdauern.&lt;br /&gt;
&lt;br /&gt;
Auf diese Sections folgen noch zwei Speicherbereiche für dynamische Daten:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;Heap&amp;lt;/tt&amp;gt;: Danach folgt der Heap. Das ist ein Speicherbereich, aus dem Speicherplatz via &amp;lt;tt&amp;gt;malloc&amp;lt;/tt&amp;gt; etc. allokiert wird.&lt;br /&gt;
;&amp;lt;tt&amp;gt;Stack&amp;lt;/tt&amp;gt;: Auf dem Stapel werden lokale Variablen/Register während Funktionsaufrufen gesichert. Der Stack wird teilweise auch zur Parameterübergabe verwendet und die return-Adresse von Funktionen und [[ISR]]s wird dort abgelegt. Der Stack beginnt an der oberen SRAM-Adresse und wächst nicht wie die andern Bereiche nach oben, sondern nach unten.&lt;br /&gt;
&lt;br /&gt;
Die Größe der ersten drei Speicherbereiche kann man für jedes Modul bereits zur Compile-Zeit bestimmen, da sie unabhängig sind von der Programmausführung. Wie viel Platz die letzten beiden Bereiche brauchen, ergibt sich erst zur Laufzeit des Programms. Diese Größen ändern sich in aller Regel mit der Zeit.&lt;br /&gt;
&lt;br /&gt;
Heap und Stack müssen sich den Speicher, der nicht von &amp;lt;tt&amp;gt;.data&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;.bss&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;.noinit&amp;lt;/tt&amp;gt; belegt ist, teilen &lt;br /&gt;
&lt;br /&gt;
Wird der Stackbereich zu groß, weil dort zu viele Daten abgelegt werden (zu viele lokale Variablen (lokale Arrays!), zu tief verschaltelte (rekursive) Funktionen, kaskadierende [[Interrupt]] Service Routinen, ...), dann überschreibt man damit womöglich andere Daten und es kommt zur Fehlfunktion des Programmes. Gleiches gilt, wenn die Obergrenze des Heap über der Untergrenze des Stabels hinauswächst.&lt;br /&gt;
&lt;br /&gt;
=Flash- und statischer RAM-Verbrauch =&lt;br /&gt;
Zur Bestimmung des Speicherplatzes, den statische Daten belegen, verwendet man &amp;lt;tt&amp;gt;avr-size&amp;lt;/tt&amp;gt;, das zu den Binutils gehört und z.B. bei [[WinAVR]] dabei ist. &lt;br /&gt;
&lt;br /&gt;
Abhängig von der [[avr-gcc#Sections|Section]] schlägt ihr Platzverbrauch in Flash/SRAM/[[EEPROM]] zu Buche:&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Blauetabelle}}&lt;br /&gt;
|+ '''Tabelle: Zuordung des Platzberbrauchs zur Section-Größe'''&lt;br /&gt;
|- {{Hintergrund1}}&lt;br /&gt;
! |belegter Speicher || Sections (Einzelgrößen addieren) ||Beschreibung&lt;br /&gt;
 |-&lt;br /&gt;
 |Flash || &amp;lt;tt&amp;gt;.text&amp;lt;/tt&amp;gt; + &amp;lt;tt&amp;gt;.bootloader&amp;lt;/tt&amp;gt; + &amp;lt;tt&amp;gt;.data&amp;lt;/tt&amp;gt; || Programmcode und Tabelle für initialisierte Daten&lt;br /&gt;
 |-&lt;br /&gt;
 |SRAM  || &amp;lt;tt&amp;gt;.data&amp;lt;/tt&amp;gt; + &amp;lt;tt&amp;gt;.bss&amp;lt;/tt&amp;gt; + &amp;lt;tt&amp;gt;.noinit&amp;lt;/tt&amp;gt;|| Daten (initialisiert + zu 0 initialisiert + nicht initialisiert)&lt;br /&gt;
 |- &lt;br /&gt;
 |EEPROM ||&amp;lt;tt&amp;gt;.eeprom&amp;lt;/tt&amp;gt;  ||Daten, die man ins EEPROM gelegt hat&lt;br /&gt;
 |}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Das &amp;lt;tt&amp;gt;'&amp;gt;'&amp;lt;/tt&amp;gt; nicht mittippen, es ist der Kommandozeilen-Prompt.&lt;br /&gt;
&lt;br /&gt;
Verbrauch der einzelnen Module auflisten:&lt;br /&gt;
 &amp;gt; avr-size -x foo1.o foo2.o ...&lt;br /&gt;
&lt;br /&gt;
Verbrauch des gesamten Programms auflisten:&lt;br /&gt;
 &amp;gt; avr-size -x -A foo.elf&lt;br /&gt;
In neueren Versionen von avr-size geht auch&lt;br /&gt;
 &amp;gt; avr-size --mcu=atmega8 -C foo.elf&lt;br /&gt;
was den Verbrauch als Absolutwerte und in Prozentangabe auflistet. Ohne die Angabe der Controllers mit &amp;lt;tt&amp;gt;--mcu&amp;lt;/tt&amp;gt; können natürlich keine Prozentwerte berechnet werden, es werden dann nur die absoluten Verbrauche ausgedruckt.&lt;br /&gt;
&lt;br /&gt;
Platzverbrauch von Funktionen, Objekten, Variablen, etc. nach Größe sortiert:&lt;br /&gt;
 &amp;gt; avr-nm --size-sort --print-size foo.elf&lt;br /&gt;
Hilfe zu avr-nm siehe: /WinAVR/doc/binutils/binutils.html/nm.html&lt;br /&gt;
&lt;br /&gt;
Zusammenfassung aus www.mikrocontroller.net:&lt;br /&gt;
&lt;br /&gt;
Großbuchstaben =&amp;gt; globale Symbole / kleine Buchstaben =&amp;gt; local symbols   &lt;br /&gt;
&lt;br /&gt;
T/t : The symbol is in the text (code) section. &lt;br /&gt;
&lt;br /&gt;
D/d : The symbol is in the initialized data section. &lt;br /&gt;
&lt;br /&gt;
B/b : The symbol is in the uninitialized data section (known as BSS). &lt;br /&gt;
&lt;br /&gt;
Alle Symbole mit einem &amp;quot;T&amp;quot; (globale Funktionen), &amp;quot;t&amp;quot; (statische Funktionen) und letztlich auch mit einem &amp;quot;D&amp;quot; oder &amp;quot;d&amp;quot; (globale bzw. statische Daten, die haben ihre Initialisierungswerte im ROM) betreffen das FLASH-ROM. &amp;quot;B&amp;quot; und &amp;quot;b&amp;quot; brauchen ausschließlich RAM (werden beim Start mit 0 initialisiert). Die erste Spalte ist die Adresse des Symbols, die zweite ist die Größe (beides hexadezimal)&lt;br /&gt;
&lt;br /&gt;
=Dynamischer RAM-Verbrauch=&lt;br /&gt;
&lt;br /&gt;
Um den momentan freien Speicher zu bestimmen, zieht man einfach den Anfang des Heaps vom Stackpointer ab:&lt;br /&gt;
 #include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 {{ccomment|__heap_start is declared in the linker script}}&lt;br /&gt;
 extern unsigned char __heap_start;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t momentan_frei = SP - (uint16_t) &amp;amp;__heap_start;&lt;br /&gt;
&lt;br /&gt;
Interessanter ist es jedoch, den Maximalverbrauch an Speicher bzw. das Minimum an freiem Speicher seit Applikationsstart zu bestimmen.&lt;br /&gt;
&lt;br /&gt;
Mit der folgenden kleinen Routine kann der noch freie SRAM-Bereich bestimmt werden. Es wird nicht der momentan freie Speicher bestimmt, sondern das Minimum an Speicher, das bis dato frei geblieben ist. Dazu wird im Startup-Code das Muster &amp;lt;tt&amp;gt;0xaa&amp;lt;/tt&amp;gt; in den SRAM geschrieben. Durch Aufruf der Funktion &amp;lt;tt&amp;gt;get_mem_unused&amp;lt;/tt&amp;gt; wird bestimmt, wieviel von diesem Muster noch intakt ist.&lt;br /&gt;
Dieses Vorgehen ist deshalb notwendig, weil es auch [[ISR]]-Routinen gibt, die dynamisch Splatz (auf dem Stack) brauchen, und man eine Routine zur Bestimmung des Speicherverbrauchs nicht in den ISRs aufrufen will.&lt;br /&gt;
&lt;br /&gt;
Mit optimierendem Compiler brauchen die beiden Routinen 42 Bytes an Flash.&lt;br /&gt;
&lt;br /&gt;
Damit der Code den richtigen Wert liefert, darf keine dynamische Speicherallokierung mit &amp;lt;tt&amp;gt;malloc()&amp;lt;/tt&amp;gt; etc. geschehen sein; ein &amp;lt;tt&amp;gt;[[Avr-gcc/Interna#Dynamische Speicherallokierung|__builtin_alloca]]&amp;lt;/tt&amp;gt; ist hingegen kein Problem, da letzteres den Platz vom Stapel nimmt. &lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;tt&amp;gt;init_mem&amp;lt;/tt&amp;gt; wird in der Init-Phase [[Avr-gcc/Interna#Frühe Codeausführung vor main()|vor &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; aufgerufen]].&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;mem-check.h&amp;lt;/tt&amp;gt;&lt;br /&gt;
 #ifndef _MEM_CHECK_H_&lt;br /&gt;
 #define _MEM_CHECK_H_&lt;br /&gt;
 &lt;br /&gt;
 extern unsigned short get_mem_unused (void);&lt;br /&gt;
 &lt;br /&gt;
 #endif  {{comment|_MEM_CHECK_H_}}&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;mem-check.c&amp;lt;/tt&amp;gt;&lt;br /&gt;
 #include &amp;lt;avr/io.h&amp;gt;  {{ccomment|RAMEND}}&lt;br /&gt;
 #include &amp;quot;mem-check.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 {{ccomment|Mask to init SRAM and check against}}&lt;br /&gt;
 #define MASK 0xaa&lt;br /&gt;
 &lt;br /&gt;
 {{ccomment|From linker script}}&lt;br /&gt;
 extern unsigned char __heap_start;&lt;br /&gt;
 &lt;br /&gt;
 unsigned short &lt;br /&gt;
 get_mem_unused (void)&lt;br /&gt;
 {&lt;br /&gt;
    unsigned short unused = 0;&lt;br /&gt;
    unsigned char *p = &amp;amp;__heap_start;&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
       if (*p++ != MASK)&lt;br /&gt;
          break;&lt;br /&gt;
 &lt;br /&gt;
       unused++;&lt;br /&gt;
    } while (p &amp;lt;= (unsigned char*) RAMEND);&lt;br /&gt;
 &lt;br /&gt;
       return unused;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 {{comment|!!! never call this function !!!}}&lt;br /&gt;
 void [[Avr-gcc/Interna#Attribute|__attribute__]] ((naked, section(&amp;quot;.init3&amp;quot;))) init_mem (void);&lt;br /&gt;
 void init_mem (void)&lt;br /&gt;
 {&lt;br /&gt;
    __asm volatile (&lt;br /&gt;
       &amp;quot;ldi r30, lo8 (__heap_start)&amp;quot;  &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;ldi r31, hi8 (__heap_start)&amp;quot;  &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;ldi r24, %0&amp;quot;                  &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;ldi r25, hi8 (%1)&amp;quot;            &amp;quot;\n&amp;quot;&lt;br /&gt;
       &amp;quot;0:&amp;quot;                           &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;st  Z+,  r24&amp;quot;                 &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;cpi r30, lo8 (%1)&amp;quot;            &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;cpc r31, r25&amp;quot;                 &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;brlo 0b&amp;quot;&lt;br /&gt;
          :&lt;br /&gt;
          : &amp;quot;i&amp;quot; (MASK), &amp;quot;i&amp;quot; (RAMEND+1)&lt;br /&gt;
    );&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Der Grund, hier auf [[Inline-Assembler in avr-gcc|Inline Assembler]] zurückzugreifen, ist folgender: Durch normalem C/C++-Code kann man nicht garantieren, daß alle Variablen in Registern leben. In diesem Falle würden Variablen, die im Frame leben, durch die init-Routine überschreiben. Davon ab wird der Frame-Pointer erst zu Anfang von &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; gesetzt und um ihn zu lesen, bräuchte man ohnehin Inline Assembler.&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
* [[AVR]]&lt;br /&gt;
* [[avr-gcc]]&lt;br /&gt;
* [[Inline-Assembler in avr-gcc|Inline-Assembler]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Quellcode C]]&lt;br /&gt;
[[Kategorie:Quellcode Assembler AVR]]&lt;br /&gt;
[[Kategorie:Software]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Speicherverbrauch_bestimmen_mit_avr-gcc&amp;diff=14556</id>
		<title>Speicherverbrauch bestimmen mit avr-gcc</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Speicherverbrauch_bestimmen_mit_avr-gcc&amp;diff=14556"/>
				<updated>2009-02-10T20:58:26Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Flash- und statischer RAM-Verbrauch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Der Speicherverbrauch eines C-Programmes lässt sich unter verschiedenen Gesichtspunkten betrachten.&lt;br /&gt;
;Ort der Datenablage:&lt;br /&gt;
* SRAM&lt;br /&gt;
* Flash&lt;br /&gt;
* EEPROM &lt;br /&gt;
&lt;br /&gt;
;Art der Daten:&lt;br /&gt;
* Programmcode&lt;br /&gt;
* Daten&lt;br /&gt;
** veränderlich/unveränderlich&lt;br /&gt;
** persistent oder flüchtig&lt;br /&gt;
&lt;br /&gt;
;Speicherklasse der Daten&lt;br /&gt;
* Statischer Speicherverbrauch&lt;br /&gt;
* Dynamischer Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
Der Ort der Datenablage wird bestimmt von der Art der Daten. Daten, die nicht verändert werden dürfen oder nicht verändert werden müssen, wie der Programm-Code oder konstante Strings, wie sie oft zur Ausgabe verwendet werden, können im Flash gespeichert werden. Konstanten/Tabellen kann man auch im EEPROM speichern, wenn der Platz im Flash knapp ist. Der Programmcode selbst muss bei [[AVR]] im Flash liegen. Man kann den Code zwar auch ins RAM oder ins EEPROM legen, aber von dort nicht ausführen.&lt;br /&gt;
&lt;br /&gt;
Daten, die zur Laufzeit verändert werden müssen, wird man im SRAM speichern, wenn sie einen Reset bzw. ein Ausschalten des Controllers nicht überleben müssen. Sollen die Daten auch ohne Strom erhalten bleiben, muss man den EEPROM oder den Flash als Ablageort wählen. Dabei ist das Speichern von Daten im Flash zur Laufzeit sehr aufwändig, weil man einen [[Bootloader]] für ihre Änderung anstrengen muss. Andererseits kann wesentlich schneller auf das Flash zugegriffen werden als auf den EEPROM-Speicher.&lt;br /&gt;
&lt;br /&gt;
Die Art der Daten ergibt sich aus dem Programm und den zu lösenden Aufgaben,&lt;br /&gt;
gleiches gilt für die Speicherklassen. &lt;br /&gt;
&lt;br /&gt;
Die Ablageorte der statische Daten sind bereits zur Compilezeit &lt;br /&gt;
bekannt. Hierzu gehören globale und statische Variablen. Dementsprechend ist auch schon zur Compile- bzw. Linkzeit bekannt, wieviel Speicher diese Daten belegen. &lt;br /&gt;
&lt;br /&gt;
Speicherorte und Platzverbrauch dynamischer Variablen sind dem Compiler nicht bekannt. Sie ergeben sich erst zur Laufzeit durch das Allokieren von Speicher mit &amp;lt;tt&amp;gt;malloc&amp;lt;/tt&amp;gt;, oder durch den Aufbau eines Stapels, auf dem lokale Variablen gesichert werden, während eine Funktion aufgerufen wird. Je nach Verschachtelungstiefe der Funktionsaufrufe &amp;amp;mdash; dazu gehören auch [[Interrupt]] Service Routinen, die prinzipiell jederzeit aufgerufen werden können &amp;amp;mdash; wird dafür auch unterschiedlich viel Speicher benötigt.&lt;br /&gt;
&lt;br /&gt;
=Nutzung des SRAM durch avr-gcc=&lt;br /&gt;
[[Bild:Avr-ram.png|right|thumb|RAM-Layout für ein AVR mit 1kByte SRAM]]&lt;br /&gt;
[[avr-gcc]] legt die Daten in [[avr-gcc/Interna#Sections|Sections]] an.&lt;br /&gt;
Nach aufsteigenden Speicheradressen sortiert sind diese:&lt;br /&gt;
;&amp;lt;tt&amp;gt;.data&amp;lt;/tt&amp;gt;: Statische und (modul-)globale, initialisierte Daten, denen man per Initializer einen Wert ungleich&amp;amp;nbsp;0 zuweist. Beginnt an der unteren SRAM-Adresse &amp;lt;tt&amp;gt;0x60&amp;lt;/tt&amp;gt; nach dem SFR-Bereich, je nach AVR-Derivat auch an anderer Adresse.&lt;br /&gt;
;&amp;lt;tt&amp;gt;.bss&amp;lt;/tt&amp;gt;: Statische und (modul-)globale, initialisierte Daten, die zu&amp;amp;nbsp;0 initialisiert sind bzw. keinen Initializer haben (und also auch zu&amp;amp;nbsp;0 initialisiert werden).&lt;br /&gt;
;&amp;lt;tt&amp;gt;.noinit&amp;lt;/tt&amp;gt;: Statische und (modul-)globale Daten, die nicht vom Startup-Code initialisiert werden und zum Beispiel einen [[Watchdog]]-Reset überdauern.&lt;br /&gt;
&lt;br /&gt;
Auf diese Sections folgen noch zwei Speicherbereiche für dynamische Daten:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;Heap&amp;lt;/tt&amp;gt;: Danach folgt der Heap. Das ist ein Speicherbereich, aus dem Speicherplatz via &amp;lt;tt&amp;gt;malloc&amp;lt;/tt&amp;gt; etc. allokiert wird.&lt;br /&gt;
;&amp;lt;tt&amp;gt;Stack&amp;lt;/tt&amp;gt;: Auf dem Stapel werden lokale Variablen/Register während Funktionsaufrufen gesichert. Der Stack wird teilweise auch zur Parameterübergabe verwendet und die return-Adresse von Funktionen und [[ISR]]s wird dort abgelegt. Der Stack beginnt an der oberen SRAM-Adresse und wächst nicht wie die andern Bereiche nach oben, sondern nach unten.&lt;br /&gt;
&lt;br /&gt;
Die Größe der ersten drei Speicherbereiche kann man für jedes Modul bereits zur Compile-Zeit bestimmen, da sie unabhängig sind von der Programmausführung. Wie viel Platz die letzten beiden Bereiche brauchen, ergibt sich erst zur Laufzeit des Programms. Diese Größen ändern sich in aller Regel mit der Zeit.&lt;br /&gt;
&lt;br /&gt;
Heap und Stack müssen sich den Speicher, der nicht von &amp;lt;tt&amp;gt;.data&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;.bss&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;.noinit&amp;lt;/tt&amp;gt; belegt ist, teilen &lt;br /&gt;
&lt;br /&gt;
Wird der Stackbereich zu groß, weil dort zu viele Daten abgelegt werden (zu viele lokale Variablen (lokale Arrays!), zu tief verschaltelte (rekursive) Funktionen, kaskadierende [[Interrupt]] Service Routinen, ...), dann überschreibt man damit womöglich andere Daten und es kommt zur Fehlfunktion des Programmes. Gleiches gilt, wenn die Obergrenze des Heap über der Untergrenze des Stabels hinauswächst.&lt;br /&gt;
&lt;br /&gt;
=Flash- und statischer RAM-Verbrauch =&lt;br /&gt;
Zur Bestimmung des Speicherplatzes, den statische Daten belegen, verwendet man &amp;lt;tt&amp;gt;avr-size&amp;lt;/tt&amp;gt;, das zu den Binutils gehört und z.B. bei [[WinAVR]] dabei ist. &lt;br /&gt;
&lt;br /&gt;
Abhängig von der [[avr-gcc#Sections|Section]] schlägt ihr Platzverbrauch in Flash/SRAM/[[EEPROM]] zu Buche:&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Blauetabelle}}&lt;br /&gt;
|+ '''Tabelle: Zuordung des Platzberbrauchs zur Section-Größe'''&lt;br /&gt;
|- {{Hintergrund1}}&lt;br /&gt;
! |belegter Speicher || Sections (Einzelgrößen addieren) ||Beschreibung&lt;br /&gt;
 |-&lt;br /&gt;
 |Flash || &amp;lt;tt&amp;gt;.text&amp;lt;/tt&amp;gt; + &amp;lt;tt&amp;gt;.bootloader&amp;lt;/tt&amp;gt; + &amp;lt;tt&amp;gt;.data&amp;lt;/tt&amp;gt; || Programmcode und Tabelle für initialisierte Daten&lt;br /&gt;
 |-&lt;br /&gt;
 |SRAM  || &amp;lt;tt&amp;gt;.data&amp;lt;/tt&amp;gt; + &amp;lt;tt&amp;gt;.bss&amp;lt;/tt&amp;gt; + &amp;lt;tt&amp;gt;.noinit&amp;lt;/tt&amp;gt;|| Daten (initialisiert + zu 0 initialisiert + nicht initialisiert)&lt;br /&gt;
 |- &lt;br /&gt;
 |EEPROM ||&amp;lt;tt&amp;gt;.eeprom&amp;lt;/tt&amp;gt;  ||Daten, die man ins EEPROM gelegt hat&lt;br /&gt;
 |}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&lt;br /&gt;
Das &amp;lt;tt&amp;gt;'&amp;gt;'&amp;lt;/tt&amp;gt; nicht mittippen, es ist der Kommandozeilen-Prompt.&lt;br /&gt;
&lt;br /&gt;
Verbrauch der einzelnen Module auflisten:&lt;br /&gt;
 &amp;gt; avr-size -x foo1.o foo2.o ...&lt;br /&gt;
&lt;br /&gt;
Verbrauch des gesamten Programms auflisten:&lt;br /&gt;
 &amp;gt; avr-size -x -A foo.elf&lt;br /&gt;
In neueren Versionen von avr-size geht auch&lt;br /&gt;
 &amp;gt; avr-size --mcu=atmega8 -C foo.elf&lt;br /&gt;
was den Verbrauch als Absolutwerte und in Prozentangabe auflistet. Ohne die Angabe der Controllers mit &amp;lt;tt&amp;gt;--mcu&amp;lt;/tt&amp;gt; können natürlich keine Prozentwerte berechnet werden, es werden dann nur die absoluten Verbrauche ausgedruckt.&lt;br /&gt;
&lt;br /&gt;
Platzverbrauch von Funktionen, Objekten, etc. nach Größe sortiert:&lt;br /&gt;
 &amp;gt; avr-nm --size-sort -S foo.elf&lt;br /&gt;
Hilfe zu avr-nm siehe: /WinAVR/doc/binutils/binutils.html/nm.html&lt;br /&gt;
&lt;br /&gt;
Zusammenfassung aus www.mikrocontroller.net:&lt;br /&gt;
Alle Symbole mit einem &amp;quot;T&amp;quot; (globale Funktionen), &amp;quot;t&amp;quot; (statische Funktionen) und letztlich auch mit einem &amp;quot;D&amp;quot; oder &amp;quot;d&amp;quot; (globale bzw. statische Daten, die haben ihre Initialisierungswerte im ROM) betreffen das FLASH-ROM. &amp;quot;B&amp;quot; und &amp;quot;b&amp;quot; brauchen ausschließlich RAM (werden beim Start mit 0 initialisiert). Die erste Spalte ist die Adresse des Symbols, die zweite ist die Größe (beides hexadezimal)&lt;br /&gt;
&lt;br /&gt;
=Dynamischer RAM-Verbrauch=&lt;br /&gt;
&lt;br /&gt;
Um den momentan freien Speicher zu bestimmen, zieht man einfach den Anfang des Heaps vom Stackpointer ab:&lt;br /&gt;
 #include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 {{ccomment|__heap_start is declared in the linker script}}&lt;br /&gt;
 extern unsigned char __heap_start;&lt;br /&gt;
    ...&lt;br /&gt;
    uint16_t momentan_frei = SP - (uint16_t) &amp;amp;__heap_start;&lt;br /&gt;
&lt;br /&gt;
Interessanter ist es jedoch, den Maximalverbrauch an Speicher bzw. das Minimum an freiem Speicher seit Applikationsstart zu bestimmen.&lt;br /&gt;
&lt;br /&gt;
Mit der folgenden kleinen Routine kann der noch freie SRAM-Bereich bestimmt werden. Es wird nicht der momentan freie Speicher bestimmt, sondern das Minimum an Speicher, das bis dato frei geblieben ist. Dazu wird im Startup-Code das Muster &amp;lt;tt&amp;gt;0xaa&amp;lt;/tt&amp;gt; in den SRAM geschrieben. Durch Aufruf der Funktion &amp;lt;tt&amp;gt;get_mem_unused&amp;lt;/tt&amp;gt; wird bestimmt, wieviel von diesem Muster noch intakt ist.&lt;br /&gt;
Dieses Vorgehen ist deshalb notwendig, weil es auch [[ISR]]-Routinen gibt, die dynamisch Splatz (auf dem Stack) brauchen, und man eine Routine zur Bestimmung des Speicherverbrauchs nicht in den ISRs aufrufen will.&lt;br /&gt;
&lt;br /&gt;
Mit optimierendem Compiler brauchen die beiden Routinen 42 Bytes an Flash.&lt;br /&gt;
&lt;br /&gt;
Damit der Code den richtigen Wert liefert, darf keine dynamische Speicherallokierung mit &amp;lt;tt&amp;gt;malloc()&amp;lt;/tt&amp;gt; etc. geschehen sein; ein &amp;lt;tt&amp;gt;[[Avr-gcc/Interna#Dynamische Speicherallokierung|__builtin_alloca]]&amp;lt;/tt&amp;gt; ist hingegen kein Problem, da letzteres den Platz vom Stapel nimmt. &lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;tt&amp;gt;init_mem&amp;lt;/tt&amp;gt; wird in der Init-Phase [[Avr-gcc/Interna#Frühe Codeausführung vor main()|vor &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; aufgerufen]].&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;mem-check.h&amp;lt;/tt&amp;gt;&lt;br /&gt;
 #ifndef _MEM_CHECK_H_&lt;br /&gt;
 #define _MEM_CHECK_H_&lt;br /&gt;
 &lt;br /&gt;
 extern unsigned short get_mem_unused (void);&lt;br /&gt;
 &lt;br /&gt;
 #endif  {{comment|_MEM_CHECK_H_}}&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;mem-check.c&amp;lt;/tt&amp;gt;&lt;br /&gt;
 #include &amp;lt;avr/io.h&amp;gt;  {{ccomment|RAMEND}}&lt;br /&gt;
 #include &amp;quot;mem-check.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 {{ccomment|Mask to init SRAM and check against}}&lt;br /&gt;
 #define MASK 0xaa&lt;br /&gt;
 &lt;br /&gt;
 {{ccomment|From linker script}}&lt;br /&gt;
 extern unsigned char __heap_start;&lt;br /&gt;
 &lt;br /&gt;
 unsigned short &lt;br /&gt;
 get_mem_unused (void)&lt;br /&gt;
 {&lt;br /&gt;
    unsigned short unused = 0;&lt;br /&gt;
    unsigned char *p = &amp;amp;__heap_start;&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
       if (*p++ != MASK)&lt;br /&gt;
          break;&lt;br /&gt;
 &lt;br /&gt;
       unused++;&lt;br /&gt;
    } while (p &amp;lt;= (unsigned char*) RAMEND);&lt;br /&gt;
 &lt;br /&gt;
       return unused;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 {{comment|!!! never call this function !!!}}&lt;br /&gt;
 void [[Avr-gcc/Interna#Attribute|__attribute__]] ((naked, section(&amp;quot;.init3&amp;quot;))) init_mem (void);&lt;br /&gt;
 void init_mem (void)&lt;br /&gt;
 {&lt;br /&gt;
    __asm volatile (&lt;br /&gt;
       &amp;quot;ldi r30, lo8 (__heap_start)&amp;quot;  &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;ldi r31, hi8 (__heap_start)&amp;quot;  &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;ldi r24, %0&amp;quot;                  &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;ldi r25, hi8 (%1)&amp;quot;            &amp;quot;\n&amp;quot;&lt;br /&gt;
       &amp;quot;0:&amp;quot;                           &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;st  Z+,  r24&amp;quot;                 &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;cpi r30, lo8 (%1)&amp;quot;            &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;cpc r31, r25&amp;quot;                 &amp;quot;\n\t&amp;quot;&lt;br /&gt;
       &amp;quot;brlo 0b&amp;quot;&lt;br /&gt;
          :&lt;br /&gt;
          : &amp;quot;i&amp;quot; (MASK), &amp;quot;i&amp;quot; (RAMEND+1)&lt;br /&gt;
    );&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Der Grund, hier auf [[Inline-Assembler in avr-gcc|Inline Assembler]] zurückzugreifen, ist folgender: Durch normalem C/C++-Code kann man nicht garantieren, daß alle Variablen in Registern leben. In diesem Falle würden Variablen, die im Frame leben, durch die init-Routine überschreiben. Davon ab wird der Frame-Pointer erst zu Anfang von &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; gesetzt und um ihn zu lesen, bräuchte man ohnehin Inline Assembler.&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
* [[AVR]]&lt;br /&gt;
* [[avr-gcc]]&lt;br /&gt;
* [[Inline-Assembler in avr-gcc|Inline-Assembler]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Quellcode C]]&lt;br /&gt;
[[Kategorie:Quellcode Assembler AVR]]&lt;br /&gt;
[[Kategorie:Software]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_und_USI-Kommunikation&amp;diff=14482</id>
		<title>Bascom und USI-Kommunikation</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_und_USI-Kommunikation&amp;diff=14482"/>
				<updated>2009-01-11T18:11:13Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* USI-I2C-Slave */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In diesem Artikel folgen einige Programmbeispiele, um das [[USI (Avr)|USI]]-Hardwaremodul der [[AVR]]s zu verwenden. Mit dem [[USI (Avr)|USI]]-Modul können die Schnittstellen [[I2C]] ([[TWI]]) und [[SPI]] nachgebildet werden. Näheres zu [[USI (Avr)|USI]], [[I2C]] und [[SPI]] finden sich in den entsprechenden Artikeln. Die Beispiele sind zwar in [[Bascom]] Basic verfasst, aber so ausgeführt, dass es möglich sein sollte das Prinzip mit jeder anderen Sprache nachvollziehen zu können.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== USI-I2C-Master ==&lt;br /&gt;
[[Bild:USI-I2C_PCF8574_RN-Control_Tiny2313.JPG|thumb|Beispielumgebung mit PCF8574]]&lt;br /&gt;
Ist der AVR Master, bestimmt er was und wie schnell es auf dem [[I2C|I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C]]-Bus zugeht. (Ausnahme: [[Clock_Stretching]] )&lt;br /&gt;
&lt;br /&gt;
Zur Generierung der gewünschten Busgeschwindigkeit ist die Software zuständig, die Länge der Pausen zwischen den Takten muss errechnet werden, im Beispielprogramm für ca. 100 kHz bei 16 MHz CPU-Frequenz.&lt;br /&gt;
Der Takt kann auch mithilfe eines [[Timer/Counter (Avr)|Timer]]s erzeugt werden, dies wird erst zu einem späteren Zeitpunkt mit einem Beispiel gezeigt.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen wird als Master das Board [[RN-Control]] mit einem [[ATtiny2313|Tiny2313]], und als Slave ein [[I2C_Chip-Übersicht#I.2FO_expanders:|PCF8574]] verwendet, da sich dieser leicht ansteuern lässt. &amp;lt;!--  und jeden Scheiss mitmacht ;-)) --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Transmitter ===&lt;br /&gt;
&lt;br /&gt;
;Nur Senden&lt;br /&gt;
&lt;br /&gt;
Der Master sendet einem Slave ein (oder mehrere) Byte, und schließt die Übertragung anschließend ab.&lt;br /&gt;
&lt;br /&gt;
Das Gegenstück wäre ''Slave Receiver''.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispielprogramm sendet ein Byte zum Slave mit Adresse 64 (0x40 bzw. &amp;amp;H40):&lt;br /&gt;
&lt;br /&gt;
 {{vbcomment|USI-I2C Testprogramm}}&lt;br /&gt;
 {{vbcomment|mit PCF8574 @ &amp;amp;H40}}&lt;br /&gt;
 {{vbcomment|}}&lt;br /&gt;
 {{vbcomment|ohne Interrupt und ohne Timer}}&lt;br /&gt;
 {{vbcomment|ein Byte senden}}&lt;br /&gt;
 $regfile = &amp;quot;attiny2313.dat&amp;quot;&lt;br /&gt;
 $crystal = 16000000&lt;br /&gt;
 $baud = 9600&lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|Unterprogramme für die USI-Kommunikation}}&lt;br /&gt;
 Declare Sub Usi_twi_master_initialise()&lt;br /&gt;
 Declare Sub Usi_twi_start_transceiver()&lt;br /&gt;
 Declare Sub Usi_twi_master_stop()&lt;br /&gt;
 Declare Sub Usi_twi_master_transfer()&lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|einige Aliases anlegen}}&lt;br /&gt;
 Pout_usi_scl Alias Portb.7&lt;br /&gt;
 Pin_usi_scl Alias Pinb.7&lt;br /&gt;
 Ddr_usi_scl Alias Ddrb.7&lt;br /&gt;
 Pout_usi_sda Alias Portb.5&lt;br /&gt;
 Pin_usi_sda Alias Pinb.5&lt;br /&gt;
 Ddr_usi_sda Alias Ddrb.5&lt;br /&gt;
 &lt;br /&gt;
 Dim Usi_twi_errorstate As Byte                          {{vbcomment|eigener Fehlerstatus}}&lt;br /&gt;
 {{vbcomment|Array der Daten die übertragen werden}}&lt;br /&gt;
 Dim Messagebuf(4) As Byte&lt;br /&gt;
 Dim Temp_usisr As Byte                                  {{vbcomment|Tempvariable für Unterprogramm}}&lt;br /&gt;
 Dim Anzahlbuf As Byte                                   {{vbcomment|Anzahl Zeichen die gesendet werden sollen }}&lt;br /&gt;
 Dim Cnt As Byte                                         {{vbcomment|Zähler}}&lt;br /&gt;
 Dim B As Byte                                           {{vbcomment|Zeichen von UART oder Testzeichen zum senden über USI}}&lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|Prepare register value to: Clear flags, and set USI to shift 8 bits i.e. count 16 clock edges.}}&lt;br /&gt;
 Const Temp_usisr_8bit = &amp;amp;HF0&lt;br /&gt;
 {{vbcomment|Prepare register value to: Clear flags, and set USI to shift 1 bit i.e. count 2 clock edges.}}&lt;br /&gt;
 Const Temp_usisr_1bit = &amp;amp;HFE&lt;br /&gt;
 &lt;br /&gt;
 B = 0&lt;br /&gt;
 Anzahlbuf = 2                                           {{vbcomment|in diesem Beispiel immer nur 2 Zeichen}}&lt;br /&gt;
 &lt;br /&gt;
 Waitms 300                                              {{vbcomment|Sicherheitspause nach Reset}}&lt;br /&gt;
 &lt;br /&gt;
 Call Usi_twi_master_initialise&lt;br /&gt;
 &lt;br /&gt;
 Print&lt;br /&gt;
 Print &amp;quot;Tiny2313 USI-TWI-Test&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 Messagebuf(1) = &amp;amp;H40                                    {{vbcomment|Adresse von 8574}}&lt;br /&gt;
 &lt;br /&gt;
 Do&lt;br /&gt;
     {{vbcomment|warten bis etwas über UART kommt}}&lt;br /&gt;
     Input B&lt;br /&gt;
     {{vbcomment|oder automatisch zählen lassen}}&lt;br /&gt;
     {{vbcomment|Incr B}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Den Wert zum Slave senden}}&lt;br /&gt;
     Messagebuf(2) = Not B                               {{vbcomment|Not, weil LEDs gegen GND schalten}}&lt;br /&gt;
     Call Usi_twi_start_transceiver&lt;br /&gt;
     Call Usi_twi_master_stop&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Ausgabe, damit wir sehen was geschehen ist}}&lt;br /&gt;
     Print B ;&lt;br /&gt;
     Print &amp;quot; &amp;quot;&lt;br /&gt;
     Print Hex(usi_twi_errorstate);&lt;br /&gt;
     Print &amp;quot; &amp;quot; ;&lt;br /&gt;
     Print Hex(temp_usisr)&lt;br /&gt;
 &lt;br /&gt;
     Call Usi_twi_master_initialise                      {{vbcomment|nochmal initialisieren falls ein Fehler aufgetreten ist}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Waitms 700  ' Wenn automatisch gezählt werden soll, eine kleine Pause, damit man auch was sehen kann}}&lt;br /&gt;
 Loop&lt;br /&gt;
 &lt;br /&gt;
 End&lt;br /&gt;
&lt;br /&gt;
==== Unterprogramm nur Senden ====&lt;br /&gt;
&lt;br /&gt;
Die restlichen Subroutinen sind im nächsten Abschnitt zu finden.&lt;br /&gt;
&lt;br /&gt;
Diese Funktion ist nur zum Versenden geeignet !&lt;br /&gt;
&lt;br /&gt;
Statt dieser kann auch die Routine von unten genommen werden, die aber etwas mehr Speicherplatz benötigt.&lt;br /&gt;
&lt;br /&gt;
 {{vbcomment|USI Transmit Funktion.}}&lt;br /&gt;
 Sub Usi_twi_start_transceiver()&lt;br /&gt;
 &lt;br /&gt;
     Usi_twi_errorstate = 0&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Test if any unexpected conditions have arrived prior to this execution.}}&lt;br /&gt;
     {{vbcomment|Ist eine Startbedingung aufgetreten ?}}&lt;br /&gt;
     If Usisr.7 = 1 Then                                 {{vbcomment|USISIF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H02                       {{vbcomment|Usi_twi_ue_start_con}}&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Ist eine Stopbedingung aufgetreten ?}}&lt;br /&gt;
     If Usisr.5 = 1 Then                                 {{vbcomment|USIPF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H03                       {{vbcomment|Usi_twi_ue_stop_con}}&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Ist eine Datenkollision aufgetreten ?}}&lt;br /&gt;
     If Usisr.4 = 1 Then                                 {{vbcomment|USIDC}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H04                       {{vbcomment|Usi_twi_ue_data_col}}&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Release SCL to ensure that (repeated) Start can be performed}}&lt;br /&gt;
     Pout_usi_scl = 1&lt;br /&gt;
     Waitus 5&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Startbedingung ausgeben}}&lt;br /&gt;
     Pout_usi_sda = 0                                    {{vbcomment|Force SDA LOW.}}&lt;br /&gt;
     Waitus 4&lt;br /&gt;
     Pout_usi_scl = 0                                    {{vbcomment|Pull SCL LOW.}}&lt;br /&gt;
     Pout_usi_sda = 1                                    {{vbcomment|Release SDA.}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|prüfen ob die Startbedingung vom eigenen USI erkannt wurde}}&lt;br /&gt;
     If Usisr.7 = 0 Then                                 {{vbcomment|USISIF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H07&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Slaveadresse und Daten ausgeben}}&lt;br /&gt;
     For Cnt = 1 To Anzahlbuf&lt;br /&gt;
         {{vbcomment|Ein Byte ausgeben}}&lt;br /&gt;
         Pout_usi_scl = 0                                {{vbcomment|Pull SCL LOW.}}&lt;br /&gt;
         Usidr = Messagebuf(cnt)&lt;br /&gt;
         Temp_usisr = Temp_usisr_8bit                    {{vbcomment|8Bit ausgeben}}&lt;br /&gt;
         Call Usi_twi_master_transfer&lt;br /&gt;
 &lt;br /&gt;
         {{vbcomment|(N)ACK vom Slave lesen}}&lt;br /&gt;
         Ddr_usi_sda = 0                                 {{vbcomment|Enable SDA as input.}}&lt;br /&gt;
         Temp_usisr = Temp_usisr_1bit                    {{vbcomment|1Bit einlesen}}&lt;br /&gt;
         Call Usi_twi_master_transfer&lt;br /&gt;
 &lt;br /&gt;
         {{vbcomment|kein ACK gekommen, Slave meldet sich nicht}}&lt;br /&gt;
         If Temp_usisr.0 = 1 Then&lt;br /&gt;
             Usi_twi_errorstate = &amp;amp;H05                   {{vbcomment|Slave hat mit NACK quittiert}}&lt;br /&gt;
             If Cnt = 1 Then&lt;br /&gt;
                 Usi_twi_errorstate = &amp;amp;H06               {{vbcomment|Es hat sich kein Slave gemeldet}}&lt;br /&gt;
             End If&lt;br /&gt;
             Exit For                                    {{vbcomment|hier wird die Schleife bei einem NACK verlasssen}}&lt;br /&gt;
         End If&lt;br /&gt;
 &lt;br /&gt;
     Next Cnt&lt;br /&gt;
 &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Transmitter und Receiver mit DS1621 als Slave ===&lt;br /&gt;
[[Bild:USI-I2C_DS1621_RN-Control_Tiny2313.JPG|thumb|Beispielumgebung mit DS1621]]&lt;br /&gt;
&lt;br /&gt;
;Senden und Empfangen (Repeated Start)&lt;br /&gt;
Im Beispiel wird ein DS1621 Temperatursensor mit I2C-Schnittstelle verwendet.&amp;lt;br&amp;gt;&lt;br /&gt;
Der Master sendet dem Slave ein Kommando, stellt um auf Empfang und holt den Temperaturwert, schließt die Übertragung anschließend ab.&lt;br /&gt;
&lt;br /&gt;
Der Temperaturwert wird über [[UART]] ([[RS232]]) im Klartext ausgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Subroutinen sind die gleichen wie für ''Master Transmitter''. Da der DS1621 aber mit 400 kHz kommunizieren kann, wurden die ''Waitus'' zwischen den Pegelwechseln weggelassen, da die Befehle hier sowieso nicht schnell genug sind und ein Wait unnötig machen. Sie sind aber als Kommentar eingefügt, damit zu sehen ist an welchen Stellen ansonsten gewartet werden sollte (zB bei 100kHz).&lt;br /&gt;
&lt;br /&gt;
Beispielprogramm holt Temperaturwert vom Slave mit Adresse 144 (0x90 bzw. &amp;amp;H90):&amp;lt;br&amp;gt;&lt;br /&gt;
(Das Beispiel belegt 1418 Byte im Flash, das sind ca. 69% eines Tiny2313 !)&lt;br /&gt;
&lt;br /&gt;
 {{vbcomment|USI-I2C Testprogramm}}&lt;br /&gt;
 {{vbcomment|mit DS1621 @ &amp;amp;H90}}&lt;br /&gt;
 {{vbcomment|}}&lt;br /&gt;
 {{vbcomment|ohne Interrupt und ohne Timer}}&lt;br /&gt;
 {{vbcomment|Temperatur von DS1621 lesen, und über UART ausgeben}}&lt;br /&gt;
 $regfile = &amp;quot;attiny2313.dat&amp;quot;&lt;br /&gt;
 $crystal = 16000000&lt;br /&gt;
 $baud = 9600&lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|Unterprogramme für die USI-Kommunikation}}&lt;br /&gt;
 Declare Sub Usi_twi_master_initialise()&lt;br /&gt;
 Declare Sub Usi_twi_start_transceiver()&lt;br /&gt;
 Declare Sub Usi_twi_master_stop()&lt;br /&gt;
 Declare Sub Usi_twi_master_transfer()&lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|einige Aliases anlegen}}&lt;br /&gt;
 Pout_usi_scl Alias Portb.7&lt;br /&gt;
 Pin_usi_scl Alias Pinb.7&lt;br /&gt;
 Ddr_usi_scl Alias Ddrb.7&lt;br /&gt;
 Pout_usi_sda Alias Portb.5&lt;br /&gt;
 Pin_usi_sda Alias Pinb.5&lt;br /&gt;
 Ddr_usi_sda Alias Ddrb.5&lt;br /&gt;
 &lt;br /&gt;
 Dim Usi_twi_errorstate As Byte                          {{vbcomment|eigener Fehlerstatus}}&lt;br /&gt;
 {{vbcomment|Array der Daten die übertragen werden}}&lt;br /&gt;
 Dim Messagebuf(4) As Byte&lt;br /&gt;
 Dim Temp_usisr As Byte                                  {{vbcomment|Tempvariable für Unterprogramm}}&lt;br /&gt;
 Dim Anzahlbuf As Byte                                   {{vbcomment|Anzahl Zeichen die gesendet werden sollen}}&lt;br /&gt;
 Dim Cnt As Byte                                         {{vbcomment|Zähler}}&lt;br /&gt;
 Dim Rw As Bit                                           {{vbcomment|Read/Write Flag}}&lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|Prepare register value to: Clear flags, and set USI to shift 8 bits i.e. count 16 clock edges.}}&lt;br /&gt;
 Const Temp_usisr_8bit = &amp;amp;HF0&lt;br /&gt;
 {{vbcomment|Prepare register value to: Clear flags, and set USI to shift 1 bit i.e. count 2 clock edges.}}&lt;br /&gt;
 Const Temp_usisr_1bit = &amp;amp;HFE&lt;br /&gt;
 &lt;br /&gt;
 Waitms 500                                              {{vbcomment|Sicherheitspause nach Reset}}&lt;br /&gt;
 &lt;br /&gt;
 Call Usi_twi_master_initialise&lt;br /&gt;
 &lt;br /&gt;
 Dim Device As Byte&lt;br /&gt;
 Dim Deviceread As Byte&lt;br /&gt;
 Dim Lowtemp As Byte&lt;br /&gt;
 Dim Hightemp As Byte&lt;br /&gt;
 &lt;br /&gt;
 Device = &amp;amp;H90&lt;br /&gt;
 Deviceread = &amp;amp;H91&lt;br /&gt;
 &lt;br /&gt;
 Sound Portd.5 , 300 , 450                               {{vbcomment|BEEP}}&lt;br /&gt;
 &lt;br /&gt;
 Print&lt;br /&gt;
 Print &amp;quot;DS1621-USI Temperatur&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|Hauptprogramm}}&lt;br /&gt;
 Do&lt;br /&gt;
 &lt;br /&gt;
     Messagebuf(1) = Device                              {{vbcomment|Adresse von DS1621}}&lt;br /&gt;
     Messagebuf(2) = &amp;amp;HEE                                {{vbcomment|Temperaturmessung anstoßen}}&lt;br /&gt;
     Anzahlbuf = 2                                       {{vbcomment|2 Byte ausgeben}}&lt;br /&gt;
     Call Usi_twi_start_transceiver&lt;br /&gt;
     {{vbcomment|Print Hex(usi_twi_errorstate)}}&lt;br /&gt;
 &lt;br /&gt;
     If Usi_twi_errorstate = 0 Then                      {{vbcomment|kein Fehler aufgetreten}}&lt;br /&gt;
 &lt;br /&gt;
         Call Usi_twi_master_stop&lt;br /&gt;
         {{vbcomment|Print Hex(usi_twi_errorstate)}}&lt;br /&gt;
 &lt;br /&gt;
         {{vbcomment|Waitms 1}}&lt;br /&gt;
         Messagebuf(1) = Device                          {{vbcomment|Adresse von DS1621}}&lt;br /&gt;
         Messagebuf(2) = &amp;amp;HAA                            {{vbcomment|Temperaturmessung Lesekommando}}&lt;br /&gt;
         Anzahlbuf = 2                                   {{vbcomment|2 Byte ausgeben}}&lt;br /&gt;
         Call Usi_twi_start_transceiver&lt;br /&gt;
         {{vbcomment|Print Hex(usi_twi_errorstate)}}&lt;br /&gt;
         {{vbcomment|kein STOP an dieser Stelle !}}&lt;br /&gt;
         {{vbcomment|es folgt ein Repeated START !}}&lt;br /&gt;
 &lt;br /&gt;
         If Usi_twi_errorstate = 0 Then                  {{vbcomment|kein Fehler aufgetreten}}&lt;br /&gt;
             {{vbcomment|Temperatur lesen}}&lt;br /&gt;
             Messagebuf(1) = Deviceread                  {{vbcomment|Adresse von DS1621}}&lt;br /&gt;
             Messagebuf(2) = 0&lt;br /&gt;
             Messagebuf(3) = 0&lt;br /&gt;
             Anzahlbuf = 3                               {{vbcomment|3 Byte ausgeben, bzw. einlesen}}&lt;br /&gt;
             Call Usi_twi_start_transceiver&lt;br /&gt;
             {{vbcomment|Print Hex(usi_twi_errorstate)}}&lt;br /&gt;
 &lt;br /&gt;
             If Usi_twi_errorstate = 0 Then              {{vbcomment|kein Fehler aufgetreten}}&lt;br /&gt;
 &lt;br /&gt;
                 Call Usi_twi_master_stop&lt;br /&gt;
                 {{vbcomment|Print Hex(usi_twi_errorstate)}}&lt;br /&gt;
 &lt;br /&gt;
                 {{vbcomment|Gelesenen Bytes stehen ab Position 2 im Array}}&lt;br /&gt;
                 Lowtemp = Messagebuf(2)&lt;br /&gt;
                 Hightemp = Messagebuf(3)&lt;br /&gt;
 &lt;br /&gt;
                 {{vbcomment|Negativer Wert ?}}&lt;br /&gt;
                 If Lowtemp.7 = 1 Then&lt;br /&gt;
                     Lowtemp = Not Lowtemp&lt;br /&gt;
                     {{vbcomment|.5 ?}}&lt;br /&gt;
                     If &amp;amp;H80 &amp;gt; Hightemp Then&lt;br /&gt;
                         Incr Lowtemp&lt;br /&gt;
                     End If&lt;br /&gt;
                     Print &amp;quot;-&amp;quot; ;&lt;br /&gt;
                 Else&lt;br /&gt;
                     Print &amp;quot; &amp;quot; ;&lt;br /&gt;
                 End If&lt;br /&gt;
 &lt;br /&gt;
                 Print Lowtemp ; &amp;quot;,&amp;quot; ;&lt;br /&gt;
 &lt;br /&gt;
                 If Hightemp = &amp;amp;H80 Then&lt;br /&gt;
                     Print &amp;quot;5&amp;quot;&lt;br /&gt;
                 Else&lt;br /&gt;
                     Print &amp;quot;0&amp;quot;&lt;br /&gt;
                 End If&lt;br /&gt;
 &lt;br /&gt;
             End If&lt;br /&gt;
         End If&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     If Usi_twi_errorstate &amp;lt;&amp;gt; 0 Then                     {{vbcomment|bei einem Fehler den Fehlercode ausgeben}}&lt;br /&gt;
         Print &amp;quot;Error &amp;quot; ; Hex(usi_twi_errorstate)&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|nochmal initialisieren falls ein Fehler aufgetreten ist}}&lt;br /&gt;
     Call Usi_twi_master_initialise&lt;br /&gt;
 &lt;br /&gt;
     Waitms 2000&lt;br /&gt;
 Loop&lt;br /&gt;
 &lt;br /&gt;
 End&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Unterprogramme für USI-Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
Hier folgen die Unterprogramme für die USI-I2C-Master Kommunikation&lt;br /&gt;
&lt;br /&gt;
==== Initialisierung ====&lt;br /&gt;
&lt;br /&gt;
USI-I2C Pins Initialisieren, und USI-Register setzen für I2C-Master.&lt;br /&gt;
&lt;br /&gt;
 {{vbcomment|USI TWI single master initialization function}}&lt;br /&gt;
 Sub Usi_twi_master_initialise()&lt;br /&gt;
     {{vbcomment|Direction Out}}&lt;br /&gt;
     Ddr_usi_scl = 1&lt;br /&gt;
     Ddr_usi_sda = 1&lt;br /&gt;
     {{vbcomment|Release SCL &amp;amp; SDA}}&lt;br /&gt;
     Pout_usi_scl = 1&lt;br /&gt;
     Pout_usi_sda = 1&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Preload dataregister with &amp;quot;released level&amp;quot; data.}}&lt;br /&gt;
     Usidr = &amp;amp;HFF&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Disable USI Interrupts.}}&lt;br /&gt;
     {{vbcomment|Set USI in Two-wire mode.}}&lt;br /&gt;
     {{vbcomment|Software stobe as counter clock source}}&lt;br /&gt;
     Usicr = &amp;amp;B00101010&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Statusflags löschen, und Counter auf 0 zurücksetzen.}}&lt;br /&gt;
     Usisr = &amp;amp;B11110000&lt;br /&gt;
 &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
==== Senden/Empfangen ====&lt;br /&gt;
&lt;br /&gt;
Startbedingung ausgeben,&amp;lt;br&amp;gt;&lt;br /&gt;
versenden der Anzahl der angegebenen Bytes,&amp;lt;br&amp;gt;&lt;br /&gt;
und einen Status zurückgeben, ob die Daten angenommen (ACK) wurden.&lt;br /&gt;
&lt;br /&gt;
 {{vbcomment|USI Transmit und Receive Funktion.}}&lt;br /&gt;
 Sub Usi_twi_start_transceiver()&lt;br /&gt;
 &lt;br /&gt;
     Usi_twi_errorstate = 0&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Test if any unexpected conditions have arrived prior to this execution.}}&lt;br /&gt;
     {{vbcomment|Ist eine Startbedingung aufgetreten ?}}&lt;br /&gt;
     If Usisr.7 = 1 Then                                 {{vbcomment|USISIF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H02                       {{vbcomment|Usi_twi_ue_start_con}}&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Ist eine Stopbedingung aufgetreten ?}}&lt;br /&gt;
     If Usisr.5 = 1 Then                                 {{vbcomment|USIPF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H03                       {{vbcomment|Usi_twi_ue_stop_con}}&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Ist eine Datenkollision aufgetreten ?}}&lt;br /&gt;
     If Usisr.4 = 1 Then                                 {{vbcomment|USIDC}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H04                       {{vbcomment|Usi_twi_ue_data_col}}&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Release SCL to ensure that (repeated) Start can be performed}}&lt;br /&gt;
     Pout_usi_scl = 1&lt;br /&gt;
     Waitus 1                                            {{vbcomment|5}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Generate Start Condition}}&lt;br /&gt;
     Pout_usi_sda = 0                                    {{vbcomment|Force SDA LOW.}}&lt;br /&gt;
     {{vbcomment|Waitus 4}}&lt;br /&gt;
     Pout_usi_scl = 0                                    {{vbcomment|Pull SCL LOW.}}&lt;br /&gt;
     Pout_usi_sda = 1                                    {{vbcomment|Release SDA.}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|prüfen ob die Startsequenz vom eigenen USI erkannt wurde}}&lt;br /&gt;
     If Usisr.7 = 0 Then                                 {{vbcomment|USISIF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H07&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|RW-Bit : Messagebuf(1).0}}&lt;br /&gt;
     Rw = Messagebuf(1).0&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Write address and Read/Write data}}&lt;br /&gt;
     For Cnt = 1 To Anzahlbuf&lt;br /&gt;
         {{vbcomment|SlaveAdresse immer Write, sonst auf R/W prüfen}}&lt;br /&gt;
         If Cnt = 1 Or Rw = 0 Then&lt;br /&gt;
             {{vbcomment|Write a byte}}&lt;br /&gt;
             Pout_usi_scl = 0                            {{vbcomment|Pull SCL LOW.}}&lt;br /&gt;
             Usidr = Messagebuf(cnt)&lt;br /&gt;
             Temp_usisr = Temp_usisr_8bit&lt;br /&gt;
             Call Usi_twi_master_transfer&lt;br /&gt;
 &lt;br /&gt;
             {{vbcomment|Clock and verify (N)ACK from slave}}&lt;br /&gt;
             Ddr_usi_sda = 0                             {{vbcomment|Enable SDA as input.}}&lt;br /&gt;
             Temp_usisr = Temp_usisr_1bit&lt;br /&gt;
             Call Usi_twi_master_transfer&lt;br /&gt;
 &lt;br /&gt;
             {{vbcomment|kein ACK gekommen}}&lt;br /&gt;
             If Temp_usisr.0 = 1 Then&lt;br /&gt;
                 Usi_twi_errorstate = &amp;amp;H05               {{vbcomment|Der Slave hat die Daten mit NACK quittiert}}&lt;br /&gt;
                 If Cnt = 1 Then&lt;br /&gt;
                     Usi_twi_errorstate = &amp;amp;H06           {{vbcomment|Es hat sich kein Slave gemeldet}}&lt;br /&gt;
                 End If&lt;br /&gt;
                 Exit For                                {{vbcomment|die Schleife bei einem Fehler verlasssen}}&lt;br /&gt;
             End If&lt;br /&gt;
         Else&lt;br /&gt;
             {{vbcomment|Read a Byte}}&lt;br /&gt;
             Ddr_usi_sda = 0                             {{vbcomment|Enable SDA as input.}}&lt;br /&gt;
             Temp_usisr = Temp_usisr_8bit&lt;br /&gt;
             Call Usi_twi_master_transfer&lt;br /&gt;
 &lt;br /&gt;
             Messagebuf(cnt) = Temp_usisr                {{vbcomment|Empfangenes Byte ins Array}}&lt;br /&gt;
 &lt;br /&gt;
             {{vbcomment|Prepare to generate ACK (or NACK in case of End Of Transmission)}}&lt;br /&gt;
             If Cnt = Anzahlbuf Then&lt;br /&gt;
                 Usidr = &amp;amp;HFF                            {{vbcomment|Load NACK to confirm End Of Transmission.}}&lt;br /&gt;
             Else&lt;br /&gt;
                 Usidr = &amp;amp;H00                            {{vbcomment|Load ACK. Set data register bit 7 (output for SDA) low.}}&lt;br /&gt;
             End If&lt;br /&gt;
 &lt;br /&gt;
             Temp_usisr = Temp_usisr_1bit&lt;br /&gt;
             Call Usi_twi_master_transfer                {{vbcomment|ACK oder NACK senden}}&lt;br /&gt;
 &lt;br /&gt;
         End If&lt;br /&gt;
     Next Cnt&lt;br /&gt;
 &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
==== Ein Byte ausgeben/einlesen ====&lt;br /&gt;
&lt;br /&gt;
Diese Routine gibt die Anzahl Bit aus, wie anhand der Variablen ''Temp_usisr'' eingestellt wurde, bzw. es wird damit die Anzahl Takte angegeben, die der Counter des USI-Moduls zählt bevor er überläuft, und das Flag ''USIOIF'' im Status-Register ''USISR'' gesetzt wird.&lt;br /&gt;
&lt;br /&gt;
Getaktet wird hier per Software, pro Bit zwei Takte. Bei jedem Takt ändert sich der Zustand (High/Low) von SCL.&lt;br /&gt;
&lt;br /&gt;
Die Daten, die ausgegeben werden sollen, müssen vor dem Aufruf dieser Routine in das Datenregister ''USIDR'' geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Ob ein Bit ausgegeben oder gelesen wird, wird vor dem Aufruf im Datenrichtungsregister der Datenleitung SDA eingestellt. Im Beispiel mit dem Alias ''Ddr_usi_sda''.&lt;br /&gt;
&lt;br /&gt;
Das gelesene Byte von ''USIDR'' wird in die Variable ''Temp_usisr'' geschrieben, und kann vom Hauptprogramm weiterverarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
 Sub Usi_twi_master_transfer()&lt;br /&gt;
     Usisr = Temp_usisr&lt;br /&gt;
 &lt;br /&gt;
     Temp_usisr = &amp;amp;B00101011                             {{vbcomment|for Toggle Clock Port.}}&lt;br /&gt;
 &lt;br /&gt;
     Do&lt;br /&gt;
         {{vbcomment|Waitus 5}}&lt;br /&gt;
         Usicr = Temp_usisr                              {{vbcomment|SCL takten}}&lt;br /&gt;
         {{vbcomment|Wait for SCL to go high.}}&lt;br /&gt;
         While Pin_usi_scl = 0&lt;br /&gt;
         Wend&lt;br /&gt;
 &lt;br /&gt;
         {{vbcomment|Waitus 4}}&lt;br /&gt;
         Usicr = Temp_usisr                              {{vbcomment|SCL takten}}&lt;br /&gt;
 &lt;br /&gt;
     Loop Until Usisr.6 = 1                              {{vbcomment|USIOIF, Wenn der Zähler überläuft, sind alle Bits versendet.}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Waitus 5}}&lt;br /&gt;
     Temp_usisr = Usidr                                  {{vbcomment|Daten aus USI-Datenregister lesen.}}&lt;br /&gt;
     Usidr = &amp;amp;HFF                                        {{vbcomment|Release SDA.}}&lt;br /&gt;
 &lt;br /&gt;
     Ddr_usi_sda = 1                                     {{vbcomment|Enable SDA as output.}}&lt;br /&gt;
 &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
==== STOP-Bedingung ausgeben ====&lt;br /&gt;
&lt;br /&gt;
STOP-Bedingung ausgeben, und prüfen ob dies vom eigenen Modul erkannt wurde.&amp;lt;br&amp;gt;&lt;br /&gt;
Status zurückgeben, ob die STOP-Bedingung erfolgreich ausgegeben wurde.&lt;br /&gt;
&lt;br /&gt;
 {{vbcomment|Function for generating a TWI Stop Condition. Used to release the TWI bus.}}&lt;br /&gt;
 Sub Usi_twi_master_stop()&lt;br /&gt;
 &lt;br /&gt;
     Pout_usi_sda = 0                                    {{vbcomment|Pull SDA LOW.}}&lt;br /&gt;
     Pout_usi_scl = 1                                    {{vbcomment|Release SCL.}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Wait for SCL to go high.}}&lt;br /&gt;
     While Pin_usi_scl = 0&lt;br /&gt;
     Wend&lt;br /&gt;
     {{vbcomment|Waitus 4}}&lt;br /&gt;
     Pout_usi_sda = 1                                    {{vbcomment|Release SDA.}}&lt;br /&gt;
     Waitus 1                                            {{vbcomment|5}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|prüfen ob die Stopsequenz vom eigenen USI erkannt wurde}}&lt;br /&gt;
     If Usisr.5 = 0 Then                                 {{vbcomment|USIPF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H08&lt;br /&gt;
 {{vbcomment|       Exit Sub                 ' Exit nicht nötig, da schon das Ende erreicht}}&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     Usisr.5 = 1                                         {{vbcomment|USIPF zurücksetzen}}&lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Fehlercodes aus den Beispielen ===&lt;br /&gt;
&lt;br /&gt;
Wenn ein unerwartetes Ereignis auftreten sollte, wird eine Zahl ausgegeben, hier ist deren Bedeutung dazu.&lt;br /&gt;
Die Fehlercodes wurden aus den Beispielen von Atmel übernommen. (siehe Weblinks)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| {{Blauetabelle}}&lt;br /&gt;
|- {{Hintergrund1}}&lt;br /&gt;
!| Code || Kurzbezeichnung || Fehlerbeschreibung&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|02&lt;br /&gt;
| USI_TWI_UE_START_CON || Unerwartete Start Bedingung aufgetreten&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|03&lt;br /&gt;
| USI_TWI_UE_STOP_CON || Unerwartete Stop Bedingung aufgetreten&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|04&lt;br /&gt;
| USI_TWI_UE_DATA_COL || Unerwartete Data Collision (arbitration)&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|05&lt;br /&gt;
| USI_TWI_NO_ACK_ON_DATA || Der Slave hat die Daten nicht per ACK quittiert&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|06&lt;br /&gt;
| USI_TWI_NO_ACK_ON_ADDRESS || Es hat sich kein Slave per ACK gemeldet&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|07&lt;br /&gt;
| USI_TWI_MISSING_START_CON || Erzeugte Start Bedingung nicht erkannt&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|08&lt;br /&gt;
| USI_TWI_MISSING_STOP_CON || Erzeugte Stop Bedingung nicht erkannt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== USI-I2C-Slave ==&lt;br /&gt;
&lt;br /&gt;
Da der I2C-Master den Takt vorgibt, sind die [[TWI#Anmerkung_zur_CPU-Frequenz|Anmerkungen zur CPU-Frequenz]] zu beachten.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen wird als Master das Board [[RN-Mega8]] mit einem [[ATMega8|Mega8]], und als Slave das [[RN-Control]] Board, mit dem Adapter auf dem der [[ATtiny2313|Tiny2313]] sitzt, verwendet. Da nur diese beiden Boards am Bus hängen, wurde ebenfalls als Slaveadresse 64 (0x40 bzw. &amp;amp;H40) verwendet, damit man die vorhergehenden Beispiele gleich weiterverwenden kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im unten angegebenen WebLink (Nr.1) zum Forum ist ein Beispielprogramm um einen USI-I2C-Slave zu programmieren, es ist noch nicht ganz ausgereift, funktioniert aber ausreichend.&lt;br /&gt;
&lt;br /&gt;
Im gleichen angegebenen WebLink (Nr.1) ist ein Beispiel für Portierung der '''Atmel Application Note AVR312''' &amp;quot;Using the USI module as a I2C slave&amp;quot; (siehe Link) auf Bascom. Das vollständig interruptgesteuerte Programm belegt 476Byte im Speicher eines ATtiny25.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Avr]] - Infos zu AVR allgemein&lt;br /&gt;
* [[I2C|I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C]] - Details zu I2C&lt;br /&gt;
* [[TWI]] - Two-wire Serial Interface&lt;br /&gt;
* [[SPI]] - Serial Peripheral Interface&lt;br /&gt;
* [[TWI Praxis]] - Bascom-Beispiele für Hardware TWI&lt;br /&gt;
* [[TWI Praxis Multimaster]] - Bascom-Beispiele für Hardware TWI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==WebLinks==&lt;br /&gt;
* [http://www.roboternetz.de/phpBB2/viewtopic.php?t=25058] - Thread im Forum der zu diesem hier geführt hat&lt;br /&gt;
* [http://www.shop.robotikhardware.de/shop/catalog/product_info.php?cPath=71&amp;amp;products_id=107] - DS1621 bei Robotikhardware&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc2561.pdf Atmel_AVR310] - Using the USI module as a I2C master&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc2560.pdf Atmel_AVR312] - Using the USI module as a I2C slave&lt;br /&gt;
* [http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=26774] - Forum-Artikel, in dem eine [[Bascom]]-Lib vorgestellt wird, um die Bascom-I2C-Befehle mit dem USI-Modul als Master zu verwenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Benutzer:Linux 80|Linux 80]] 02:11, 26. Nov 2006 (CET)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Abkürzung]]&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:Kommunikation]]&lt;br /&gt;
[[Kategorie:Microcontroller]]&lt;br /&gt;
[[Kategorie:Elektronik]]&lt;br /&gt;
[[Kategorie:Praxis]]&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_und_USI-Kommunikation&amp;diff=14481</id>
		<title>Bascom und USI-Kommunikation</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_und_USI-Kommunikation&amp;diff=14481"/>
				<updated>2009-01-11T18:09:13Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* USI-I2C-Slave */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In diesem Artikel folgen einige Programmbeispiele, um das [[USI (Avr)|USI]]-Hardwaremodul der [[AVR]]s zu verwenden. Mit dem [[USI (Avr)|USI]]-Modul können die Schnittstellen [[I2C]] ([[TWI]]) und [[SPI]] nachgebildet werden. Näheres zu [[USI (Avr)|USI]], [[I2C]] und [[SPI]] finden sich in den entsprechenden Artikeln. Die Beispiele sind zwar in [[Bascom]] Basic verfasst, aber so ausgeführt, dass es möglich sein sollte das Prinzip mit jeder anderen Sprache nachvollziehen zu können.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== USI-I2C-Master ==&lt;br /&gt;
[[Bild:USI-I2C_PCF8574_RN-Control_Tiny2313.JPG|thumb|Beispielumgebung mit PCF8574]]&lt;br /&gt;
Ist der AVR Master, bestimmt er was und wie schnell es auf dem [[I2C|I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C]]-Bus zugeht. (Ausnahme: [[Clock_Stretching]] )&lt;br /&gt;
&lt;br /&gt;
Zur Generierung der gewünschten Busgeschwindigkeit ist die Software zuständig, die Länge der Pausen zwischen den Takten muss errechnet werden, im Beispielprogramm für ca. 100 kHz bei 16 MHz CPU-Frequenz.&lt;br /&gt;
Der Takt kann auch mithilfe eines [[Timer/Counter (Avr)|Timer]]s erzeugt werden, dies wird erst zu einem späteren Zeitpunkt mit einem Beispiel gezeigt.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen wird als Master das Board [[RN-Control]] mit einem [[ATtiny2313|Tiny2313]], und als Slave ein [[I2C_Chip-Übersicht#I.2FO_expanders:|PCF8574]] verwendet, da sich dieser leicht ansteuern lässt. &amp;lt;!--  und jeden Scheiss mitmacht ;-)) --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Transmitter ===&lt;br /&gt;
&lt;br /&gt;
;Nur Senden&lt;br /&gt;
&lt;br /&gt;
Der Master sendet einem Slave ein (oder mehrere) Byte, und schließt die Übertragung anschließend ab.&lt;br /&gt;
&lt;br /&gt;
Das Gegenstück wäre ''Slave Receiver''.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispielprogramm sendet ein Byte zum Slave mit Adresse 64 (0x40 bzw. &amp;amp;H40):&lt;br /&gt;
&lt;br /&gt;
 {{vbcomment|USI-I2C Testprogramm}}&lt;br /&gt;
 {{vbcomment|mit PCF8574 @ &amp;amp;H40}}&lt;br /&gt;
 {{vbcomment|}}&lt;br /&gt;
 {{vbcomment|ohne Interrupt und ohne Timer}}&lt;br /&gt;
 {{vbcomment|ein Byte senden}}&lt;br /&gt;
 $regfile = &amp;quot;attiny2313.dat&amp;quot;&lt;br /&gt;
 $crystal = 16000000&lt;br /&gt;
 $baud = 9600&lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|Unterprogramme für die USI-Kommunikation}}&lt;br /&gt;
 Declare Sub Usi_twi_master_initialise()&lt;br /&gt;
 Declare Sub Usi_twi_start_transceiver()&lt;br /&gt;
 Declare Sub Usi_twi_master_stop()&lt;br /&gt;
 Declare Sub Usi_twi_master_transfer()&lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|einige Aliases anlegen}}&lt;br /&gt;
 Pout_usi_scl Alias Portb.7&lt;br /&gt;
 Pin_usi_scl Alias Pinb.7&lt;br /&gt;
 Ddr_usi_scl Alias Ddrb.7&lt;br /&gt;
 Pout_usi_sda Alias Portb.5&lt;br /&gt;
 Pin_usi_sda Alias Pinb.5&lt;br /&gt;
 Ddr_usi_sda Alias Ddrb.5&lt;br /&gt;
 &lt;br /&gt;
 Dim Usi_twi_errorstate As Byte                          {{vbcomment|eigener Fehlerstatus}}&lt;br /&gt;
 {{vbcomment|Array der Daten die übertragen werden}}&lt;br /&gt;
 Dim Messagebuf(4) As Byte&lt;br /&gt;
 Dim Temp_usisr As Byte                                  {{vbcomment|Tempvariable für Unterprogramm}}&lt;br /&gt;
 Dim Anzahlbuf As Byte                                   {{vbcomment|Anzahl Zeichen die gesendet werden sollen }}&lt;br /&gt;
 Dim Cnt As Byte                                         {{vbcomment|Zähler}}&lt;br /&gt;
 Dim B As Byte                                           {{vbcomment|Zeichen von UART oder Testzeichen zum senden über USI}}&lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|Prepare register value to: Clear flags, and set USI to shift 8 bits i.e. count 16 clock edges.}}&lt;br /&gt;
 Const Temp_usisr_8bit = &amp;amp;HF0&lt;br /&gt;
 {{vbcomment|Prepare register value to: Clear flags, and set USI to shift 1 bit i.e. count 2 clock edges.}}&lt;br /&gt;
 Const Temp_usisr_1bit = &amp;amp;HFE&lt;br /&gt;
 &lt;br /&gt;
 B = 0&lt;br /&gt;
 Anzahlbuf = 2                                           {{vbcomment|in diesem Beispiel immer nur 2 Zeichen}}&lt;br /&gt;
 &lt;br /&gt;
 Waitms 300                                              {{vbcomment|Sicherheitspause nach Reset}}&lt;br /&gt;
 &lt;br /&gt;
 Call Usi_twi_master_initialise&lt;br /&gt;
 &lt;br /&gt;
 Print&lt;br /&gt;
 Print &amp;quot;Tiny2313 USI-TWI-Test&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 Messagebuf(1) = &amp;amp;H40                                    {{vbcomment|Adresse von 8574}}&lt;br /&gt;
 &lt;br /&gt;
 Do&lt;br /&gt;
     {{vbcomment|warten bis etwas über UART kommt}}&lt;br /&gt;
     Input B&lt;br /&gt;
     {{vbcomment|oder automatisch zählen lassen}}&lt;br /&gt;
     {{vbcomment|Incr B}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Den Wert zum Slave senden}}&lt;br /&gt;
     Messagebuf(2) = Not B                               {{vbcomment|Not, weil LEDs gegen GND schalten}}&lt;br /&gt;
     Call Usi_twi_start_transceiver&lt;br /&gt;
     Call Usi_twi_master_stop&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Ausgabe, damit wir sehen was geschehen ist}}&lt;br /&gt;
     Print B ;&lt;br /&gt;
     Print &amp;quot; &amp;quot;&lt;br /&gt;
     Print Hex(usi_twi_errorstate);&lt;br /&gt;
     Print &amp;quot; &amp;quot; ;&lt;br /&gt;
     Print Hex(temp_usisr)&lt;br /&gt;
 &lt;br /&gt;
     Call Usi_twi_master_initialise                      {{vbcomment|nochmal initialisieren falls ein Fehler aufgetreten ist}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Waitms 700  ' Wenn automatisch gezählt werden soll, eine kleine Pause, damit man auch was sehen kann}}&lt;br /&gt;
 Loop&lt;br /&gt;
 &lt;br /&gt;
 End&lt;br /&gt;
&lt;br /&gt;
==== Unterprogramm nur Senden ====&lt;br /&gt;
&lt;br /&gt;
Die restlichen Subroutinen sind im nächsten Abschnitt zu finden.&lt;br /&gt;
&lt;br /&gt;
Diese Funktion ist nur zum Versenden geeignet !&lt;br /&gt;
&lt;br /&gt;
Statt dieser kann auch die Routine von unten genommen werden, die aber etwas mehr Speicherplatz benötigt.&lt;br /&gt;
&lt;br /&gt;
 {{vbcomment|USI Transmit Funktion.}}&lt;br /&gt;
 Sub Usi_twi_start_transceiver()&lt;br /&gt;
 &lt;br /&gt;
     Usi_twi_errorstate = 0&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Test if any unexpected conditions have arrived prior to this execution.}}&lt;br /&gt;
     {{vbcomment|Ist eine Startbedingung aufgetreten ?}}&lt;br /&gt;
     If Usisr.7 = 1 Then                                 {{vbcomment|USISIF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H02                       {{vbcomment|Usi_twi_ue_start_con}}&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Ist eine Stopbedingung aufgetreten ?}}&lt;br /&gt;
     If Usisr.5 = 1 Then                                 {{vbcomment|USIPF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H03                       {{vbcomment|Usi_twi_ue_stop_con}}&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Ist eine Datenkollision aufgetreten ?}}&lt;br /&gt;
     If Usisr.4 = 1 Then                                 {{vbcomment|USIDC}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H04                       {{vbcomment|Usi_twi_ue_data_col}}&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Release SCL to ensure that (repeated) Start can be performed}}&lt;br /&gt;
     Pout_usi_scl = 1&lt;br /&gt;
     Waitus 5&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Startbedingung ausgeben}}&lt;br /&gt;
     Pout_usi_sda = 0                                    {{vbcomment|Force SDA LOW.}}&lt;br /&gt;
     Waitus 4&lt;br /&gt;
     Pout_usi_scl = 0                                    {{vbcomment|Pull SCL LOW.}}&lt;br /&gt;
     Pout_usi_sda = 1                                    {{vbcomment|Release SDA.}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|prüfen ob die Startbedingung vom eigenen USI erkannt wurde}}&lt;br /&gt;
     If Usisr.7 = 0 Then                                 {{vbcomment|USISIF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H07&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Slaveadresse und Daten ausgeben}}&lt;br /&gt;
     For Cnt = 1 To Anzahlbuf&lt;br /&gt;
         {{vbcomment|Ein Byte ausgeben}}&lt;br /&gt;
         Pout_usi_scl = 0                                {{vbcomment|Pull SCL LOW.}}&lt;br /&gt;
         Usidr = Messagebuf(cnt)&lt;br /&gt;
         Temp_usisr = Temp_usisr_8bit                    {{vbcomment|8Bit ausgeben}}&lt;br /&gt;
         Call Usi_twi_master_transfer&lt;br /&gt;
 &lt;br /&gt;
         {{vbcomment|(N)ACK vom Slave lesen}}&lt;br /&gt;
         Ddr_usi_sda = 0                                 {{vbcomment|Enable SDA as input.}}&lt;br /&gt;
         Temp_usisr = Temp_usisr_1bit                    {{vbcomment|1Bit einlesen}}&lt;br /&gt;
         Call Usi_twi_master_transfer&lt;br /&gt;
 &lt;br /&gt;
         {{vbcomment|kein ACK gekommen, Slave meldet sich nicht}}&lt;br /&gt;
         If Temp_usisr.0 = 1 Then&lt;br /&gt;
             Usi_twi_errorstate = &amp;amp;H05                   {{vbcomment|Slave hat mit NACK quittiert}}&lt;br /&gt;
             If Cnt = 1 Then&lt;br /&gt;
                 Usi_twi_errorstate = &amp;amp;H06               {{vbcomment|Es hat sich kein Slave gemeldet}}&lt;br /&gt;
             End If&lt;br /&gt;
             Exit For                                    {{vbcomment|hier wird die Schleife bei einem NACK verlasssen}}&lt;br /&gt;
         End If&lt;br /&gt;
 &lt;br /&gt;
     Next Cnt&lt;br /&gt;
 &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Transmitter und Receiver mit DS1621 als Slave ===&lt;br /&gt;
[[Bild:USI-I2C_DS1621_RN-Control_Tiny2313.JPG|thumb|Beispielumgebung mit DS1621]]&lt;br /&gt;
&lt;br /&gt;
;Senden und Empfangen (Repeated Start)&lt;br /&gt;
Im Beispiel wird ein DS1621 Temperatursensor mit I2C-Schnittstelle verwendet.&amp;lt;br&amp;gt;&lt;br /&gt;
Der Master sendet dem Slave ein Kommando, stellt um auf Empfang und holt den Temperaturwert, schließt die Übertragung anschließend ab.&lt;br /&gt;
&lt;br /&gt;
Der Temperaturwert wird über [[UART]] ([[RS232]]) im Klartext ausgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Subroutinen sind die gleichen wie für ''Master Transmitter''. Da der DS1621 aber mit 400 kHz kommunizieren kann, wurden die ''Waitus'' zwischen den Pegelwechseln weggelassen, da die Befehle hier sowieso nicht schnell genug sind und ein Wait unnötig machen. Sie sind aber als Kommentar eingefügt, damit zu sehen ist an welchen Stellen ansonsten gewartet werden sollte (zB bei 100kHz).&lt;br /&gt;
&lt;br /&gt;
Beispielprogramm holt Temperaturwert vom Slave mit Adresse 144 (0x90 bzw. &amp;amp;H90):&amp;lt;br&amp;gt;&lt;br /&gt;
(Das Beispiel belegt 1418 Byte im Flash, das sind ca. 69% eines Tiny2313 !)&lt;br /&gt;
&lt;br /&gt;
 {{vbcomment|USI-I2C Testprogramm}}&lt;br /&gt;
 {{vbcomment|mit DS1621 @ &amp;amp;H90}}&lt;br /&gt;
 {{vbcomment|}}&lt;br /&gt;
 {{vbcomment|ohne Interrupt und ohne Timer}}&lt;br /&gt;
 {{vbcomment|Temperatur von DS1621 lesen, und über UART ausgeben}}&lt;br /&gt;
 $regfile = &amp;quot;attiny2313.dat&amp;quot;&lt;br /&gt;
 $crystal = 16000000&lt;br /&gt;
 $baud = 9600&lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|Unterprogramme für die USI-Kommunikation}}&lt;br /&gt;
 Declare Sub Usi_twi_master_initialise()&lt;br /&gt;
 Declare Sub Usi_twi_start_transceiver()&lt;br /&gt;
 Declare Sub Usi_twi_master_stop()&lt;br /&gt;
 Declare Sub Usi_twi_master_transfer()&lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|einige Aliases anlegen}}&lt;br /&gt;
 Pout_usi_scl Alias Portb.7&lt;br /&gt;
 Pin_usi_scl Alias Pinb.7&lt;br /&gt;
 Ddr_usi_scl Alias Ddrb.7&lt;br /&gt;
 Pout_usi_sda Alias Portb.5&lt;br /&gt;
 Pin_usi_sda Alias Pinb.5&lt;br /&gt;
 Ddr_usi_sda Alias Ddrb.5&lt;br /&gt;
 &lt;br /&gt;
 Dim Usi_twi_errorstate As Byte                          {{vbcomment|eigener Fehlerstatus}}&lt;br /&gt;
 {{vbcomment|Array der Daten die übertragen werden}}&lt;br /&gt;
 Dim Messagebuf(4) As Byte&lt;br /&gt;
 Dim Temp_usisr As Byte                                  {{vbcomment|Tempvariable für Unterprogramm}}&lt;br /&gt;
 Dim Anzahlbuf As Byte                                   {{vbcomment|Anzahl Zeichen die gesendet werden sollen}}&lt;br /&gt;
 Dim Cnt As Byte                                         {{vbcomment|Zähler}}&lt;br /&gt;
 Dim Rw As Bit                                           {{vbcomment|Read/Write Flag}}&lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|Prepare register value to: Clear flags, and set USI to shift 8 bits i.e. count 16 clock edges.}}&lt;br /&gt;
 Const Temp_usisr_8bit = &amp;amp;HF0&lt;br /&gt;
 {{vbcomment|Prepare register value to: Clear flags, and set USI to shift 1 bit i.e. count 2 clock edges.}}&lt;br /&gt;
 Const Temp_usisr_1bit = &amp;amp;HFE&lt;br /&gt;
 &lt;br /&gt;
 Waitms 500                                              {{vbcomment|Sicherheitspause nach Reset}}&lt;br /&gt;
 &lt;br /&gt;
 Call Usi_twi_master_initialise&lt;br /&gt;
 &lt;br /&gt;
 Dim Device As Byte&lt;br /&gt;
 Dim Deviceread As Byte&lt;br /&gt;
 Dim Lowtemp As Byte&lt;br /&gt;
 Dim Hightemp As Byte&lt;br /&gt;
 &lt;br /&gt;
 Device = &amp;amp;H90&lt;br /&gt;
 Deviceread = &amp;amp;H91&lt;br /&gt;
 &lt;br /&gt;
 Sound Portd.5 , 300 , 450                               {{vbcomment|BEEP}}&lt;br /&gt;
 &lt;br /&gt;
 Print&lt;br /&gt;
 Print &amp;quot;DS1621-USI Temperatur&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 {{vbcomment|Hauptprogramm}}&lt;br /&gt;
 Do&lt;br /&gt;
 &lt;br /&gt;
     Messagebuf(1) = Device                              {{vbcomment|Adresse von DS1621}}&lt;br /&gt;
     Messagebuf(2) = &amp;amp;HEE                                {{vbcomment|Temperaturmessung anstoßen}}&lt;br /&gt;
     Anzahlbuf = 2                                       {{vbcomment|2 Byte ausgeben}}&lt;br /&gt;
     Call Usi_twi_start_transceiver&lt;br /&gt;
     {{vbcomment|Print Hex(usi_twi_errorstate)}}&lt;br /&gt;
 &lt;br /&gt;
     If Usi_twi_errorstate = 0 Then                      {{vbcomment|kein Fehler aufgetreten}}&lt;br /&gt;
 &lt;br /&gt;
         Call Usi_twi_master_stop&lt;br /&gt;
         {{vbcomment|Print Hex(usi_twi_errorstate)}}&lt;br /&gt;
 &lt;br /&gt;
         {{vbcomment|Waitms 1}}&lt;br /&gt;
         Messagebuf(1) = Device                          {{vbcomment|Adresse von DS1621}}&lt;br /&gt;
         Messagebuf(2) = &amp;amp;HAA                            {{vbcomment|Temperaturmessung Lesekommando}}&lt;br /&gt;
         Anzahlbuf = 2                                   {{vbcomment|2 Byte ausgeben}}&lt;br /&gt;
         Call Usi_twi_start_transceiver&lt;br /&gt;
         {{vbcomment|Print Hex(usi_twi_errorstate)}}&lt;br /&gt;
         {{vbcomment|kein STOP an dieser Stelle !}}&lt;br /&gt;
         {{vbcomment|es folgt ein Repeated START !}}&lt;br /&gt;
 &lt;br /&gt;
         If Usi_twi_errorstate = 0 Then                  {{vbcomment|kein Fehler aufgetreten}}&lt;br /&gt;
             {{vbcomment|Temperatur lesen}}&lt;br /&gt;
             Messagebuf(1) = Deviceread                  {{vbcomment|Adresse von DS1621}}&lt;br /&gt;
             Messagebuf(2) = 0&lt;br /&gt;
             Messagebuf(3) = 0&lt;br /&gt;
             Anzahlbuf = 3                               {{vbcomment|3 Byte ausgeben, bzw. einlesen}}&lt;br /&gt;
             Call Usi_twi_start_transceiver&lt;br /&gt;
             {{vbcomment|Print Hex(usi_twi_errorstate)}}&lt;br /&gt;
 &lt;br /&gt;
             If Usi_twi_errorstate = 0 Then              {{vbcomment|kein Fehler aufgetreten}}&lt;br /&gt;
 &lt;br /&gt;
                 Call Usi_twi_master_stop&lt;br /&gt;
                 {{vbcomment|Print Hex(usi_twi_errorstate)}}&lt;br /&gt;
 &lt;br /&gt;
                 {{vbcomment|Gelesenen Bytes stehen ab Position 2 im Array}}&lt;br /&gt;
                 Lowtemp = Messagebuf(2)&lt;br /&gt;
                 Hightemp = Messagebuf(3)&lt;br /&gt;
 &lt;br /&gt;
                 {{vbcomment|Negativer Wert ?}}&lt;br /&gt;
                 If Lowtemp.7 = 1 Then&lt;br /&gt;
                     Lowtemp = Not Lowtemp&lt;br /&gt;
                     {{vbcomment|.5 ?}}&lt;br /&gt;
                     If &amp;amp;H80 &amp;gt; Hightemp Then&lt;br /&gt;
                         Incr Lowtemp&lt;br /&gt;
                     End If&lt;br /&gt;
                     Print &amp;quot;-&amp;quot; ;&lt;br /&gt;
                 Else&lt;br /&gt;
                     Print &amp;quot; &amp;quot; ;&lt;br /&gt;
                 End If&lt;br /&gt;
 &lt;br /&gt;
                 Print Lowtemp ; &amp;quot;,&amp;quot; ;&lt;br /&gt;
 &lt;br /&gt;
                 If Hightemp = &amp;amp;H80 Then&lt;br /&gt;
                     Print &amp;quot;5&amp;quot;&lt;br /&gt;
                 Else&lt;br /&gt;
                     Print &amp;quot;0&amp;quot;&lt;br /&gt;
                 End If&lt;br /&gt;
 &lt;br /&gt;
             End If&lt;br /&gt;
         End If&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     If Usi_twi_errorstate &amp;lt;&amp;gt; 0 Then                     {{vbcomment|bei einem Fehler den Fehlercode ausgeben}}&lt;br /&gt;
         Print &amp;quot;Error &amp;quot; ; Hex(usi_twi_errorstate)&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|nochmal initialisieren falls ein Fehler aufgetreten ist}}&lt;br /&gt;
     Call Usi_twi_master_initialise&lt;br /&gt;
 &lt;br /&gt;
     Waitms 2000&lt;br /&gt;
 Loop&lt;br /&gt;
 &lt;br /&gt;
 End&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Unterprogramme für USI-Kommunikation ===&lt;br /&gt;
&lt;br /&gt;
Hier folgen die Unterprogramme für die USI-I2C-Master Kommunikation&lt;br /&gt;
&lt;br /&gt;
==== Initialisierung ====&lt;br /&gt;
&lt;br /&gt;
USI-I2C Pins Initialisieren, und USI-Register setzen für I2C-Master.&lt;br /&gt;
&lt;br /&gt;
 {{vbcomment|USI TWI single master initialization function}}&lt;br /&gt;
 Sub Usi_twi_master_initialise()&lt;br /&gt;
     {{vbcomment|Direction Out}}&lt;br /&gt;
     Ddr_usi_scl = 1&lt;br /&gt;
     Ddr_usi_sda = 1&lt;br /&gt;
     {{vbcomment|Release SCL &amp;amp; SDA}}&lt;br /&gt;
     Pout_usi_scl = 1&lt;br /&gt;
     Pout_usi_sda = 1&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Preload dataregister with &amp;quot;released level&amp;quot; data.}}&lt;br /&gt;
     Usidr = &amp;amp;HFF&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Disable USI Interrupts.}}&lt;br /&gt;
     {{vbcomment|Set USI in Two-wire mode.}}&lt;br /&gt;
     {{vbcomment|Software stobe as counter clock source}}&lt;br /&gt;
     Usicr = &amp;amp;B00101010&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Statusflags löschen, und Counter auf 0 zurücksetzen.}}&lt;br /&gt;
     Usisr = &amp;amp;B11110000&lt;br /&gt;
 &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
==== Senden/Empfangen ====&lt;br /&gt;
&lt;br /&gt;
Startbedingung ausgeben,&amp;lt;br&amp;gt;&lt;br /&gt;
versenden der Anzahl der angegebenen Bytes,&amp;lt;br&amp;gt;&lt;br /&gt;
und einen Status zurückgeben, ob die Daten angenommen (ACK) wurden.&lt;br /&gt;
&lt;br /&gt;
 {{vbcomment|USI Transmit und Receive Funktion.}}&lt;br /&gt;
 Sub Usi_twi_start_transceiver()&lt;br /&gt;
 &lt;br /&gt;
     Usi_twi_errorstate = 0&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Test if any unexpected conditions have arrived prior to this execution.}}&lt;br /&gt;
     {{vbcomment|Ist eine Startbedingung aufgetreten ?}}&lt;br /&gt;
     If Usisr.7 = 1 Then                                 {{vbcomment|USISIF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H02                       {{vbcomment|Usi_twi_ue_start_con}}&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Ist eine Stopbedingung aufgetreten ?}}&lt;br /&gt;
     If Usisr.5 = 1 Then                                 {{vbcomment|USIPF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H03                       {{vbcomment|Usi_twi_ue_stop_con}}&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Ist eine Datenkollision aufgetreten ?}}&lt;br /&gt;
     If Usisr.4 = 1 Then                                 {{vbcomment|USIDC}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H04                       {{vbcomment|Usi_twi_ue_data_col}}&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Release SCL to ensure that (repeated) Start can be performed}}&lt;br /&gt;
     Pout_usi_scl = 1&lt;br /&gt;
     Waitus 1                                            {{vbcomment|5}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Generate Start Condition}}&lt;br /&gt;
     Pout_usi_sda = 0                                    {{vbcomment|Force SDA LOW.}}&lt;br /&gt;
     {{vbcomment|Waitus 4}}&lt;br /&gt;
     Pout_usi_scl = 0                                    {{vbcomment|Pull SCL LOW.}}&lt;br /&gt;
     Pout_usi_sda = 1                                    {{vbcomment|Release SDA.}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|prüfen ob die Startsequenz vom eigenen USI erkannt wurde}}&lt;br /&gt;
     If Usisr.7 = 0 Then                                 {{vbcomment|USISIF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H07&lt;br /&gt;
         Exit Sub&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|RW-Bit : Messagebuf(1).0}}&lt;br /&gt;
     Rw = Messagebuf(1).0&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Write address and Read/Write data}}&lt;br /&gt;
     For Cnt = 1 To Anzahlbuf&lt;br /&gt;
         {{vbcomment|SlaveAdresse immer Write, sonst auf R/W prüfen}}&lt;br /&gt;
         If Cnt = 1 Or Rw = 0 Then&lt;br /&gt;
             {{vbcomment|Write a byte}}&lt;br /&gt;
             Pout_usi_scl = 0                            {{vbcomment|Pull SCL LOW.}}&lt;br /&gt;
             Usidr = Messagebuf(cnt)&lt;br /&gt;
             Temp_usisr = Temp_usisr_8bit&lt;br /&gt;
             Call Usi_twi_master_transfer&lt;br /&gt;
 &lt;br /&gt;
             {{vbcomment|Clock and verify (N)ACK from slave}}&lt;br /&gt;
             Ddr_usi_sda = 0                             {{vbcomment|Enable SDA as input.}}&lt;br /&gt;
             Temp_usisr = Temp_usisr_1bit&lt;br /&gt;
             Call Usi_twi_master_transfer&lt;br /&gt;
 &lt;br /&gt;
             {{vbcomment|kein ACK gekommen}}&lt;br /&gt;
             If Temp_usisr.0 = 1 Then&lt;br /&gt;
                 Usi_twi_errorstate = &amp;amp;H05               {{vbcomment|Der Slave hat die Daten mit NACK quittiert}}&lt;br /&gt;
                 If Cnt = 1 Then&lt;br /&gt;
                     Usi_twi_errorstate = &amp;amp;H06           {{vbcomment|Es hat sich kein Slave gemeldet}}&lt;br /&gt;
                 End If&lt;br /&gt;
                 Exit For                                {{vbcomment|die Schleife bei einem Fehler verlasssen}}&lt;br /&gt;
             End If&lt;br /&gt;
         Else&lt;br /&gt;
             {{vbcomment|Read a Byte}}&lt;br /&gt;
             Ddr_usi_sda = 0                             {{vbcomment|Enable SDA as input.}}&lt;br /&gt;
             Temp_usisr = Temp_usisr_8bit&lt;br /&gt;
             Call Usi_twi_master_transfer&lt;br /&gt;
 &lt;br /&gt;
             Messagebuf(cnt) = Temp_usisr                {{vbcomment|Empfangenes Byte ins Array}}&lt;br /&gt;
 &lt;br /&gt;
             {{vbcomment|Prepare to generate ACK (or NACK in case of End Of Transmission)}}&lt;br /&gt;
             If Cnt = Anzahlbuf Then&lt;br /&gt;
                 Usidr = &amp;amp;HFF                            {{vbcomment|Load NACK to confirm End Of Transmission.}}&lt;br /&gt;
             Else&lt;br /&gt;
                 Usidr = &amp;amp;H00                            {{vbcomment|Load ACK. Set data register bit 7 (output for SDA) low.}}&lt;br /&gt;
             End If&lt;br /&gt;
 &lt;br /&gt;
             Temp_usisr = Temp_usisr_1bit&lt;br /&gt;
             Call Usi_twi_master_transfer                {{vbcomment|ACK oder NACK senden}}&lt;br /&gt;
 &lt;br /&gt;
         End If&lt;br /&gt;
     Next Cnt&lt;br /&gt;
 &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
==== Ein Byte ausgeben/einlesen ====&lt;br /&gt;
&lt;br /&gt;
Diese Routine gibt die Anzahl Bit aus, wie anhand der Variablen ''Temp_usisr'' eingestellt wurde, bzw. es wird damit die Anzahl Takte angegeben, die der Counter des USI-Moduls zählt bevor er überläuft, und das Flag ''USIOIF'' im Status-Register ''USISR'' gesetzt wird.&lt;br /&gt;
&lt;br /&gt;
Getaktet wird hier per Software, pro Bit zwei Takte. Bei jedem Takt ändert sich der Zustand (High/Low) von SCL.&lt;br /&gt;
&lt;br /&gt;
Die Daten, die ausgegeben werden sollen, müssen vor dem Aufruf dieser Routine in das Datenregister ''USIDR'' geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Ob ein Bit ausgegeben oder gelesen wird, wird vor dem Aufruf im Datenrichtungsregister der Datenleitung SDA eingestellt. Im Beispiel mit dem Alias ''Ddr_usi_sda''.&lt;br /&gt;
&lt;br /&gt;
Das gelesene Byte von ''USIDR'' wird in die Variable ''Temp_usisr'' geschrieben, und kann vom Hauptprogramm weiterverarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
 Sub Usi_twi_master_transfer()&lt;br /&gt;
     Usisr = Temp_usisr&lt;br /&gt;
 &lt;br /&gt;
     Temp_usisr = &amp;amp;B00101011                             {{vbcomment|for Toggle Clock Port.}}&lt;br /&gt;
 &lt;br /&gt;
     Do&lt;br /&gt;
         {{vbcomment|Waitus 5}}&lt;br /&gt;
         Usicr = Temp_usisr                              {{vbcomment|SCL takten}}&lt;br /&gt;
         {{vbcomment|Wait for SCL to go high.}}&lt;br /&gt;
         While Pin_usi_scl = 0&lt;br /&gt;
         Wend&lt;br /&gt;
 &lt;br /&gt;
         {{vbcomment|Waitus 4}}&lt;br /&gt;
         Usicr = Temp_usisr                              {{vbcomment|SCL takten}}&lt;br /&gt;
 &lt;br /&gt;
     Loop Until Usisr.6 = 1                              {{vbcomment|USIOIF, Wenn der Zähler überläuft, sind alle Bits versendet.}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Waitus 5}}&lt;br /&gt;
     Temp_usisr = Usidr                                  {{vbcomment|Daten aus USI-Datenregister lesen.}}&lt;br /&gt;
     Usidr = &amp;amp;HFF                                        {{vbcomment|Release SDA.}}&lt;br /&gt;
 &lt;br /&gt;
     Ddr_usi_sda = 1                                     {{vbcomment|Enable SDA as output.}}&lt;br /&gt;
 &lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
==== STOP-Bedingung ausgeben ====&lt;br /&gt;
&lt;br /&gt;
STOP-Bedingung ausgeben, und prüfen ob dies vom eigenen Modul erkannt wurde.&amp;lt;br&amp;gt;&lt;br /&gt;
Status zurückgeben, ob die STOP-Bedingung erfolgreich ausgegeben wurde.&lt;br /&gt;
&lt;br /&gt;
 {{vbcomment|Function for generating a TWI Stop Condition. Used to release the TWI bus.}}&lt;br /&gt;
 Sub Usi_twi_master_stop()&lt;br /&gt;
 &lt;br /&gt;
     Pout_usi_sda = 0                                    {{vbcomment|Pull SDA LOW.}}&lt;br /&gt;
     Pout_usi_scl = 1                                    {{vbcomment|Release SCL.}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|Wait for SCL to go high.}}&lt;br /&gt;
     While Pin_usi_scl = 0&lt;br /&gt;
     Wend&lt;br /&gt;
     {{vbcomment|Waitus 4}}&lt;br /&gt;
     Pout_usi_sda = 1                                    {{vbcomment|Release SDA.}}&lt;br /&gt;
     Waitus 1                                            {{vbcomment|5}}&lt;br /&gt;
 &lt;br /&gt;
     {{vbcomment|prüfen ob die Stopsequenz vom eigenen USI erkannt wurde}}&lt;br /&gt;
     If Usisr.5 = 0 Then                                 {{vbcomment|USIPF}}&lt;br /&gt;
         Usi_twi_errorstate = &amp;amp;H08&lt;br /&gt;
 {{vbcomment|       Exit Sub                 ' Exit nicht nötig, da schon das Ende erreicht}}&lt;br /&gt;
     End If&lt;br /&gt;
 &lt;br /&gt;
     Usisr.5 = 1                                         {{vbcomment|USIPF zurücksetzen}}&lt;br /&gt;
 End Sub&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=== Fehlercodes aus den Beispielen ===&lt;br /&gt;
&lt;br /&gt;
Wenn ein unerwartetes Ereignis auftreten sollte, wird eine Zahl ausgegeben, hier ist deren Bedeutung dazu.&lt;br /&gt;
Die Fehlercodes wurden aus den Beispielen von Atmel übernommen. (siehe Weblinks)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{| {{Blauetabelle}}&lt;br /&gt;
|- {{Hintergrund1}}&lt;br /&gt;
!| Code || Kurzbezeichnung || Fehlerbeschreibung&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|02&lt;br /&gt;
| USI_TWI_UE_START_CON || Unerwartete Start Bedingung aufgetreten&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|03&lt;br /&gt;
| USI_TWI_UE_STOP_CON || Unerwartete Stop Bedingung aufgetreten&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|04&lt;br /&gt;
| USI_TWI_UE_DATA_COL || Unerwartete Data Collision (arbitration)&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|05&lt;br /&gt;
| USI_TWI_NO_ACK_ON_DATA || Der Slave hat die Daten nicht per ACK quittiert&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|06&lt;br /&gt;
| USI_TWI_NO_ACK_ON_ADDRESS || Es hat sich kein Slave per ACK gemeldet&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|07&lt;br /&gt;
| USI_TWI_MISSING_START_CON || Erzeugte Start Bedingung nicht erkannt&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|08&lt;br /&gt;
| USI_TWI_MISSING_STOP_CON || Erzeugte Stop Bedingung nicht erkannt&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== USI-I2C-Slave ==&lt;br /&gt;
&lt;br /&gt;
Da der I2C-Master den Takt vorgibt, sind die [[TWI#Anmerkung_zur_CPU-Frequenz|Anmerkungen zur CPU-Frequenz]] zu beachten.&lt;br /&gt;
&lt;br /&gt;
In den Beispielen wird als Master das Board [[RN-Mega8]] mit einem [[ATMega8|Mega8]], und als Slave das [[RN-Control]] Board, mit dem Adapter auf dem der [[ATtiny2313|Tiny2313]] sitzt, verwendet. Da nur diese beiden Boards am Bus hängen, wurde ebenfalls als Slaveadresse 64 (0x40 bzw. &amp;amp;H40) verwendet, damit man die vorhergehenden Beispiele gleich weiterverwenden kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im unten angegebenen WebLink (Nr.1) zum Forum ist ein Beispielprogramm um einen USI-I2C-Slave zu programmieren, es ist noch nicht ganz ausgereift, funktioniert aber ausreichend.&lt;br /&gt;
&lt;br /&gt;
Im gleichen angegebenen WebLink (Nr.1) ist ein Beispiel für Portierung der Atmel App Note AVR312 &amp;quot;Using the USI module as a I2C slave&amp;quot; (siehe Link) auf Bascom. Das vollständig interruptgesteuerte Programm belegt 476Byte im Speicher eines ATtiny25.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Avr]] - Infos zu AVR allgemein&lt;br /&gt;
* [[I2C|I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C]] - Details zu I2C&lt;br /&gt;
* [[TWI]] - Two-wire Serial Interface&lt;br /&gt;
* [[SPI]] - Serial Peripheral Interface&lt;br /&gt;
* [[TWI Praxis]] - Bascom-Beispiele für Hardware TWI&lt;br /&gt;
* [[TWI Praxis Multimaster]] - Bascom-Beispiele für Hardware TWI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==WebLinks==&lt;br /&gt;
* [http://www.roboternetz.de/phpBB2/viewtopic.php?t=25058] - Thread im Forum der zu diesem hier geführt hat&lt;br /&gt;
* [http://www.shop.robotikhardware.de/shop/catalog/product_info.php?cPath=71&amp;amp;products_id=107] - DS1621 bei Robotikhardware&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc2561.pdf Atmel_AVR310] - Using the USI module as a I2C master&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc2560.pdf Atmel_AVR312] - Using the USI module as a I2C slave&lt;br /&gt;
* [http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=26774] - Forum-Artikel, in dem eine [[Bascom]]-Lib vorgestellt wird, um die Bascom-I2C-Befehle mit dem USI-Modul als Master zu verwenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[Benutzer:Linux 80|Linux 80]] 02:11, 26. Nov 2006 (CET)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Abkürzung]]&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:Kommunikation]]&lt;br /&gt;
[[Kategorie:Microcontroller]]&lt;br /&gt;
[[Kategorie:Elektronik]]&lt;br /&gt;
[[Kategorie:Praxis]]&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Libraries&amp;diff=14480</id>
		<title>Bascom Libraries</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Libraries&amp;diff=14480"/>
				<updated>2009-01-11T15:45:53Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Bascom Libraries==&lt;br /&gt;
&lt;br /&gt;
Im Installationsbereich von Bascom befinden sich eine Reihe Libraries mit den Endungen &amp;quot;.LBX&amp;quot; und &amp;quot;.LIB&amp;quot;. Erstere sind gleichnamige, aber vorkompilierte Versionen von den &amp;quot;.LIB&amp;quot; Files. Dieses Vorkompilieren geschieht im LIB-Manager (Menu-&amp;gt;Tools). &lt;br /&gt;
&lt;br /&gt;
Aber auch die &amp;quot;.LBX&amp;quot; Files sind keine Object-Libraries, wie man sie von anderen Sprachen her kennt und die durch einen &amp;quot;Linker&amp;quot; zusammengefügt werden. Beides sind normale Textfiles, die erst beim Kompilieren des Basic-Hauptprogrammes tatächlich in Maschinencode übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
Deshalb ist es Bascom auch ziemlich egal, ob man bei der $lib Anweisung &amp;quot;lib.lib&amp;quot; oder &amp;quot;lib.lbx&amp;quot; angibt, es ist im Grunde die selbe Arbeit&lt;br /&gt;
&lt;br /&gt;
Warum nun überhaupt &amp;quot;.LBX&amp;quot; ?&lt;br /&gt;
&lt;br /&gt;
Durch das Vorkompilieren werden die Libraries doch einigermassen unleserlich, und wenn von einer Library die &amp;quot;.LIB&amp;quot; Version nicht da ist, will man den Code wohl nicht preisgeben. &lt;br /&gt;
&lt;br /&gt;
Beide Versionen sind Sammlungen von &amp;quot;Bausteinen&amp;quot;, die Bascom nur in das Benutzer-Programm einbindet, wenn sie tatsächlich gebraucht werden. Diese Bausteine sehen folgendermassen aus: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 [Baustein_Name]&lt;br /&gt;
 Baustein_Name:&lt;br /&gt;
 .... Assembler-Answeisungen...&lt;br /&gt;
 RET&lt;br /&gt;
 [end]&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
&lt;br /&gt;
Man erzeugt mit Notepad oder einem anderen Editor eine Datei &amp;quot;Testlib.LIB&amp;quot; im Library-Verzeichnis von Bascom (muß man im Installationsverzeichnis nachsehen, wo das ist)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Meine_Funktion]&lt;br /&gt;
Meine_Funktion:&lt;br /&gt;
  RET&lt;br /&gt;
[end]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
(und speichern, natürlich)&lt;br /&gt;
&lt;br /&gt;
Im Hauptprogramm (in BasCom) schreibt man nun&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = .....&lt;br /&gt;
$lib &amp;quot;testlib.lib&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$external Meine_Funktion&lt;br /&gt;
Declare Sub Meine_Funktion()&lt;br /&gt;
&lt;br /&gt;
'.... irgendwo...&lt;br /&gt;
CALL Meine_Funktion&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Dadurch wird die Funktion an die nächste freie Stelle in den Maschinen-Code eingefügt und bei &amp;quot;Call&amp;quot; aufgerufen.  &lt;br /&gt;
&lt;br /&gt;
Man braucht jetzt nur noch irgendetwas Sinnvolles in die Funktion reinzuschreiben.&lt;br /&gt;
&lt;br /&gt;
===Ersetzen von Bascom-Library-Functions===&lt;br /&gt;
Es ist sehr einfach, Funktionen, die sich in irgendeiner mitgelieferten Bascom-Library befinden, durch eigenen Code zu ersetzen. Bascom geht nämlich beim Suchen nach einer gerade benötigten Funktion im in der Reihenfolge der &amp;quot;$LIB&amp;quot; Anweisung vor. Die Standardlibrary &amp;quot;MCS.LBX&amp;quot; wird immer erst als Letztes durchsucht. Schreibt man also &lt;br /&gt;
 $LIB &amp;quot;testlib.lib&amp;quot;&lt;br /&gt;
sucht er erst in dieser Library, und erst wenn da nix drinsteht, nimmt er das, was sich in MCS.LIB &lt;br /&gt;
befindet. &lt;br /&gt;
&lt;br /&gt;
Leider sind nicht alle Bascom-Funktionen in Libraries vorhanden, vieles ist im BasCom-Programm fest eingebaut. Und manches findet man erst nach ein wenig Detektivarbeit (&amp;quot;PULSEIN&amp;quot; heißt zum Beispiel &amp;quot;_PULSE_IN&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
===Tips===&lt;br /&gt;
'''Vorgangsweise'''&lt;br /&gt;
&lt;br /&gt;
Wenn man eine Library-Funktion entwickeln will, ist es günstig, sie erstmal als normale Sub-Routine mit inline-Assembler zu schreiben, und erst, wenn das halbwegs klappt, sie in eine Library zu übertragen. Die Syntax ist zwar etwas verschieden, aber das kriegt man hin. Der Vorteil ist aber, daß man seinen Assemblercode im Simulator normal debuggen kann, und das ist was wert.   &lt;br /&gt;
&lt;br /&gt;
'''Argumente'''&lt;br /&gt;
&lt;br /&gt;
Wenn man der Funktion Argumente oder eine Result-Adresse übergeben will, muß man natürlich den &lt;br /&gt;
[[Bascom_Inside#Call_Sub_.26_Function|Bascom Call-Standard]] beachten. Der ist aber recht aufwendig. Alle (globalen) Variablen, die man mit DIM angelegt hat, kann man auch mit &amp;quot;LOADADR&amp;quot; adressieren, das spart Einiges. &lt;br /&gt;
&lt;br /&gt;
'''Sichern'''&lt;br /&gt;
&lt;br /&gt;
Zwischen &amp;quot;$ASM&amp;quot; und &amp;quot;$END ASM&amp;quot;  ist man Herrscher über den kompletten Controller. Man kann alle Register hemmungslos einsetzen. Es ist also nicht so wie in einer ISR, daß man alles sichern und wieder herstellen muß &lt;br /&gt;
&lt;br /&gt;
Nur die Register&lt;br /&gt;
 YL:YH&lt;br /&gt;
 R4:R5&lt;br /&gt;
 R6&lt;br /&gt;
sollte man sichern und am Ende wieder herstellen, wenn man sie verändern will.&lt;br /&gt;
&lt;br /&gt;
==Tutorial:Eine Library-Funktion erstellen==&lt;br /&gt;
Es soll der ganze Vorgang der Entwicklung einmal durchgezogen werden. &lt;br /&gt;
===Mem_move===&lt;br /&gt;
Diese Beispiel-Funktion soll einfach ein SRAM-Bereich in ein anderes kopieren, das Ziel ist immer ein Byte-Array, die Quelle hingegen sollen Daten mit beliebigen Datentypen sein. Die Übertragungslänge muss man daher natürlich auch angeben. Als Ergebnis wiederum die Anzahl der übertragenen Bytes. Das ist in diesem Beispiel natürlich sinnlos, aber wir wollen ja auch sehen, wie man einen Wert zurückgeben kann. &lt;br /&gt;
&lt;br /&gt;
 Declare Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte&lt;br /&gt;
&lt;br /&gt;
Soweit alles klar, &amp;quot;Target&amp;quot; ist das Zielarray, &amp;quot;Length&amp;quot; die Übertragungslänge, nur was hat es mit &amp;quot;''Srcaddr As Word''&amp;quot; auf sich ? Es sollen ja beliebige Daten und nicht nur ein WORD übertragen werden. &lt;br /&gt;
&lt;br /&gt;
Das ist ja ein Haken bei Bascom, das geht eben nicht so direkt. &lt;br /&gt;
&lt;br /&gt;
Wir müssen die Funktion-Deklaration ja mit einem konkreten Datentyp befüllen, und den Typ &amp;quot;irgendwas&amp;quot; gibt's eben nicht. &lt;br /&gt;
&lt;br /&gt;
Also macht man sowas mit &amp;quot;die Adresse von der Adresse&amp;quot; (C-Programmierer kennen das). D.h. wir definieren ein WORD, schreiben die aktuelle Datenquell-Adresse rein, und unsere Funktion kriegt immer die Adresse von diesem Word. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Dim Source As Word&lt;br /&gt;
&lt;br /&gt;
   Source = Varptr(source_string)                 ' Die SRAM-Adresse von &amp;quot;Source_String&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Beispiel Hauptprogramm===&lt;br /&gt;
(für einen Atmeg32, das ist aber völlig egal). Ein paar Dinge sind noch dabei, damit man mit &amp;quot;print&amp;quot; auch im Simulator-Terminal die Sache kontrollieren kann. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
$baud = 9600&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
$swstack = 256&lt;br /&gt;
$framesize = 64&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Declare Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dim Ziel_len As Byte                                 ' da soll das Ergebnis (Länge) rein&lt;br /&gt;
Dim Ziel_arr(24) As Byte                             ' hier soll hinkopiert werden.&lt;br /&gt;
&lt;br /&gt;
Dim Print_string As String * 24 At Ziel_arr Overlay  ' das ist nur für den &amp;quot;PRINT&amp;quot;, um das &lt;br /&gt;
                                                     ' Zielarray auf einfach herzuzeigen&lt;br /&gt;
&lt;br /&gt;
Dim Source_string As String * 24                     ' das werden die Ziel-daten &lt;br /&gt;
&lt;br /&gt;
   Source_string = &amp;quot;Hello, world&amp;quot;                    ' damit auch was drinsteht&lt;br /&gt;
&lt;br /&gt;
Dim Source As Word                                   ' siehe oben&lt;br /&gt;
&lt;br /&gt;
   Source = Varptr(source_string)&lt;br /&gt;
&lt;br /&gt;
   Ziel_len = Mem_move(ziel_arr(1) , Source , 5)     ' der funktionsaufruf: &lt;br /&gt;
                                                     ' 5 Byte von Source kopieren&lt;br /&gt;
&lt;br /&gt;
   Print &amp;quot;From &amp;quot; ; Source_string                         ' so sieht der Source-String aus&lt;br /&gt;
   Print &amp;quot;To  (&amp;quot; ; Str(ziel_len) ; &amp;quot;) &amp;quot; ; Print_string   ' und so die kopierten Daten&lt;br /&gt;
&lt;br /&gt;
End                                                   ' keine Schleife, wozu auch ?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Step 1: Inline Assembler===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Die Funktions Argumente:'''&lt;br /&gt;
&lt;br /&gt;
Nochmals der Verweis auf den [[Bascom_Inside#Call_Sub_.26_Function|Bascom Call-Standard]]. Am Anfang der Funktion findet diese ihre Argumente im &amp;quot;SoftStack&amp;quot;, das Pointer-Registerpaar Y (YL:YH) zeigt auf den Beginn.  &lt;br /&gt;
&lt;br /&gt;
Bascom legt die Addressen der Argumente absteigend in folgender Reihenfolge auf den Soft-Stack:&lt;br /&gt;
&lt;br /&gt;
 Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte&lt;br /&gt;
&lt;br /&gt;
Erst die Adresse vom Funktionsergebnis, dann von links beginnend die Argument-Adressen in der Klammer, also insgesamt:&lt;br /&gt;
* High(Result) &lt;br /&gt;
* Low(Result)&lt;br /&gt;
* High(Target)&lt;br /&gt;
* Low(Target)&lt;br /&gt;
* High(Srcaddr)&lt;br /&gt;
* Low(Srcaddr)&lt;br /&gt;
* High(Length)&lt;br /&gt;
* Low(Length)&lt;br /&gt;
Und auf diesen letzten Wert zeigt YL:YH beim Aufruf der Funktion. Gelesen wird das mit &lt;br /&gt;
 LD   register, Y + Offset&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   ldi    r24, 65&lt;br /&gt;
   ldd    xl, y + 6   &lt;br /&gt;
   ldd    xh, y + 7 &lt;br /&gt;
   st     x, r24&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Entspricht dem Bascom-Statement&lt;br /&gt;
 Mem_move = 65&lt;br /&gt;
d.h. die Funktion würde das Ergebnis 65 liefern&lt;br /&gt;
&lt;br /&gt;
'''Und nun die Funktion &amp;quot;inline&amp;quot;'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte&lt;br /&gt;
   $asm&lt;br /&gt;
   ldd    xl, y + 0                               'length addr lo&lt;br /&gt;
   ldd    xh, y + 1                               'length addr high&lt;br /&gt;
   ld     r24, x                                  'Length --&amp;gt; R24&lt;br /&gt;
   clr    r25                                     'Löschen Zählregister&lt;br /&gt;
   !and     r24, r24                              'length = 0 ?&lt;br /&gt;
   breq     Mem_mov_xit                           'wenn die Länge = 0 , ist garnix zu tun&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Erst wir Xl:XH mit der adresse des Längenarguments geladen, dann Register 24 mit dem eigentlichen Wert.&lt;br /&gt;
Der Zähler für das Funktionsergebnis wird auf Null gesetzt, dann wird die angegebene Länge geprüft, ob überhaupt was zu tun ist. &lt;br /&gt;
&lt;br /&gt;
(Anm: das Rufzeichen beim &amp;quot;and&amp;quot; zeigt dem Bascom, daß das ein &amp;quot;Assembler-And&amp;quot; ist und ihn nix angeht)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   ldd    xl, y + 2                               'source PARAM&lt;br /&gt;
   ldd    xh, y + 3                               'source PARAM&lt;br /&gt;
   ld     zl, x+                                  'source&lt;br /&gt;
   ld     zh, x                                   'source&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Für &amp;quot;Source&amp;quot; ist eine Ecke mehr: Auf dem Stack befindete sich die Adresse von einem WORD. Und erst da drin steht die eigentliche Adresse der Source-Daten.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   ldd    xl, y + 4                               'target&lt;br /&gt;
   ldd    xh, y + 5                               'target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das ist der &amp;quot;Normalfall&amp;quot;. Auf dem Stack steht direkt die Zieladresse ( Ziel_arr(1) )&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mem_mov_lop:&lt;br /&gt;
   ld    r22, Z+                                  'read source&lt;br /&gt;
   st    x+, r22                                  'write target&lt;br /&gt;
   inc   r25                                      'counter++&lt;br /&gt;
   dec   r24                                      'length--&lt;br /&gt;
   brne  Mem_mov_lop                              '&amp;lt;&amp;gt; 0 ? --&amp;gt;loop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Copy-Schleife: von Z mit autoinkrement ins Register 22, von da nach X, auch mit Inkrement. Jedesmal wir der Ergebniszähler erhöht und der Kontrollzähler vermindert.  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mem_mov_xit:&lt;br /&gt;
   ldd    xl, y + 6                               ' result&lt;br /&gt;
   ldd    xh, y + 7                               ' result&lt;br /&gt;
   st       x, r25&lt;br /&gt;
   $end Asm&lt;br /&gt;
End Function&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
X wird mit der Adresse des Funktionsergebnisses geladen (s.o) und R25 wird dorthingeschrieben. &lt;br /&gt;
&lt;br /&gt;
'''Testen im Simulator'''&lt;br /&gt;
Man sollte sich die tatsächlichen Variablen-Adressen aus der &amp;quot;.RPT&amp;quot; File anschauen. Damit und mit dem Register- und Memory-Fenster des Simulators kann man recht gut die einzelnen Steps beobachten. Für die Printout's beim &amp;quot;Teminal&amp;quot; ein Häkchen machen !&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Step 2: Library Function===&lt;br /&gt;
Angenommen, wir sind zufrieden, dann machen wir nun eine Library. Ich mach das mit Word- oder Notepad, ist egal, ein Text-Editor halt. Geht auch mit Bascom selbst, ist aber Geschmackssache&lt;br /&gt;
&lt;br /&gt;
'''Eine leere Library'''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
copyright = Anybody&lt;br /&gt;
www       = http://www.roboternetz.de&lt;br /&gt;
email     = picnick@nowhere.at&lt;br /&gt;
comment   = BASCOM AVR compiler library for Demo&lt;br /&gt;
libversion   = 0.1&lt;br /&gt;
date         = 04 jun 2006&lt;br /&gt;
history      = Beta&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Und nun mit Cut &amp;amp; Paste aus dem Bascom Editor die Funktion einfügern&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
copyright = Anybody&lt;br /&gt;
www       = http://www.roboternetz.de&lt;br /&gt;
email     = picnick@nowhere.at&lt;br /&gt;
comment   = BASCOM AVR compiler library for Demo&lt;br /&gt;
libversion   = 0.1&lt;br /&gt;
date         = 04 jun 2006&lt;br /&gt;
history      = Beta&lt;br /&gt;
&lt;br /&gt;
; Declare Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte&lt;br /&gt;
&lt;br /&gt;
[Mem_move]&lt;br /&gt;
Mem_move:&lt;br /&gt;
   ldd    xl, y + 0                               'length&lt;br /&gt;
   ldd    xh, y + 1                               'length&lt;br /&gt;
   ld     r24, x&lt;br /&gt;
   clr    r25                                     ' clear counter&lt;br /&gt;
   and     r24, r24                              'length = 0 ?&lt;br /&gt;
   breq     Mem_mov_xit                           ' no action&lt;br /&gt;
&lt;br /&gt;
   ldd    xl, y + 2                               'source PARAM&lt;br /&gt;
   ldd    xh, y + 3                               'source PARAM&lt;br /&gt;
   ld     zl, x+                                  'source&lt;br /&gt;
   ld     zh, x                                   'source&lt;br /&gt;
&lt;br /&gt;
   ldd    xl, y + 4                               'target&lt;br /&gt;
   ldd    xh, y + 5                               'target&lt;br /&gt;
&lt;br /&gt;
Mem_mov_lop:&lt;br /&gt;
   ld    r22, Z+                                  'read source&lt;br /&gt;
   st    x+, r22                                  'write target&lt;br /&gt;
   inc   r25                                      'counter++&lt;br /&gt;
   dec   r24                                      'length--&lt;br /&gt;
   brne  Mem_mov_lop                              '&amp;lt;&amp;gt; 0 ? --&amp;gt;loop&lt;br /&gt;
&lt;br /&gt;
Mem_mov_xit:&lt;br /&gt;
   ldd    xl, y + 6                               ' result&lt;br /&gt;
   ldd    xh, y + 7                               ' result&lt;br /&gt;
   st       x, r25&lt;br /&gt;
&lt;br /&gt;
  ret         ; ready&lt;br /&gt;
[end]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das Rufzeichen bei &amp;quot;AND&amp;quot; lassen wir weg, dafür kommt ist das dazugekommen. Ich rate, die Funktions-Deklaration als Kommentar dazuzuschreiben.  &lt;br /&gt;
 [Mem_move]&lt;br /&gt;
 Mem_move:&lt;br /&gt;
 .....&lt;br /&gt;
  ret         ; ready&lt;br /&gt;
 [end]&lt;br /&gt;
Das &amp;quot;RET&amp;quot; hat bei der Inline Version der Bascom gemacht (&amp;quot;END FUNCTION&amp;quot;), das darf man nun nicht vergessen.&lt;br /&gt;
&lt;br /&gt;
Und nun &amp;quot;speichern als&amp;quot; RN_LIBRARY.LIB  bei den anderen Bascom Libraries. Bei mir ist das z.B. &lt;br /&gt;
 E:\Programme\MCS Electronics\BASCOM-AVR\LIB\RN_LIBRARY.LIB&lt;br /&gt;
 Aufpassen mit dem File-Typ !&lt;br /&gt;
&lt;br /&gt;
Wir können das nun schon kontrollieren, im Bascom bei &amp;quot;Tool&amp;quot; ist ein Lib-Manager, das muß diese Library nun zu sehen sein.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Hauptprogramm umbauen'''&lt;br /&gt;
Das sieht nun so aus &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
$baud = 9600&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
$swstack = 256&lt;br /&gt;
$framesize = 64&lt;br /&gt;
&lt;br /&gt;
$lib &amp;quot;RN_Library.LIB&amp;quot;                 ' das ist neu &lt;br /&gt;
$external Mem_move                    ' das ist neu &lt;br /&gt;
&lt;br /&gt;
Declare Function Mem_move(target As Byte , Srcaddr As Word , Byval Length As Byte) As Byte&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dim Ziel_len As Byte&lt;br /&gt;
Dim Ziel_arr(24) As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Print_string As String * 24 At Ziel_arr Overlay&lt;br /&gt;
Dim Source_string As String * 24&lt;br /&gt;
&lt;br /&gt;
Dim Source As Word&lt;br /&gt;
&lt;br /&gt;
   Source_string = &amp;quot;Hello, world&amp;quot;&lt;br /&gt;
&lt;br /&gt;
   Source = Varptr(source_string)&lt;br /&gt;
   Ziel_len = Mem_move(ziel_arr(1) , Source , 5)&lt;br /&gt;
&lt;br /&gt;
   Print &amp;quot;From &amp;quot; ; Source_string&lt;br /&gt;
   Print &amp;quot;To  (&amp;quot; ; Str(ziel_len) ; &amp;quot;) &amp;quot; ; Print_string&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun &amp;quot;F7&amp;quot; drücken, wird sowohl das Basic-Programm als auch die Library übersetzt. &lt;br /&gt;
&lt;br /&gt;
 Das war's eigentlich&lt;br /&gt;
&lt;br /&gt;
===LBX ?===&lt;br /&gt;
Von wegen &amp;quot;.LBX&amp;quot;: Wenn wir mit dem Lib-Manager die Library kompilieren lassen, sieht das Ergebnis so aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Comment = Compiled LIB file, no comment included&lt;br /&gt;
&lt;br /&gt;
copyright = Anybody&lt;br /&gt;
www       = http://www.roboternetz.de&lt;br /&gt;
email     = PicNick@nowhere.at&lt;br /&gt;
comment   = BASCOM AVR compiler library for Demo&lt;br /&gt;
libversion   = 0.1&lt;br /&gt;
date         = 04 jun 2006&lt;br /&gt;
history      = Beta&lt;br /&gt;
[Mem_move]&lt;br /&gt;
Mem_move:&lt;br /&gt;
.OBJ 81A8&lt;br /&gt;
.OBJ 81B9&lt;br /&gt;
.OBJ 918C&lt;br /&gt;
.OBJ 2799&lt;br /&gt;
.OBJ 2388&lt;br /&gt;
   breq     Mem_mov_xit                           ' no action&lt;br /&gt;
.OBJ 81AA&lt;br /&gt;
.OBJ 81BB&lt;br /&gt;
.OBJ 91ED&lt;br /&gt;
.OBJ 91FC&lt;br /&gt;
.OBJ 81AC&lt;br /&gt;
.OBJ 81BD&lt;br /&gt;
Mem_mov_lop:&lt;br /&gt;
.OBJ 9161&lt;br /&gt;
.OBJ 936D&lt;br /&gt;
.OBJ 9593&lt;br /&gt;
.OBJ 958A&lt;br /&gt;
   brne  Mem_mov_lop                              '&amp;lt;&amp;gt; 0 ? --&amp;gt;loop&lt;br /&gt;
Mem_mov_xit:&lt;br /&gt;
.OBJ 81AE&lt;br /&gt;
.OBJ 81BF&lt;br /&gt;
.OBJ 939C&lt;br /&gt;
.OBJ 9508&lt;br /&gt;
[end]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit kann man sein know-how vor neugierigen Blicken verstecken. Um das zu verwenden, muß man im Hauptprogramm aber dann schreiben: &lt;br /&gt;
 $LIB &amp;quot;RN_LIBRARY.LBX&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Anmerkung==&lt;br /&gt;
Library-Funktionen können auch ohne den Call-Standard verwendet werden. D.h. die Sache mit der Argumenten-Übergabe über den Softstack ist nicht zwingend vorgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Man könnte das auch folgendermaßen machen und einigen Code sparen, den das Aufbauen des SoftStacks und das Lesen der Argumente in der Funktion selbst sind recht aufwendig. &lt;br /&gt;
&lt;br /&gt;
In unserem Beispiel lief ja im Kern der Copy über die Pointer-register Z u. X mit dem Zähler r24 &lt;br /&gt;
&lt;br /&gt;
'''Hauptprogramm'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$LIB = &amp;quot;library.lib&amp;quot;&lt;br /&gt;
$external move_z_to_x&lt;br /&gt;
Declare Sub move_z_to_x()&lt;br /&gt;
&lt;br /&gt;
     $asm&lt;br /&gt;
     LDI    r24, 5&lt;br /&gt;
     $end asm&lt;br /&gt;
     LOADADR array(1), X         ' das werden nur 2 Maschinenbefehle !&lt;br /&gt;
     LOADADR source_string, Z    ' ebenso&lt;br /&gt;
     GOSUB  move_z_to_x          ' das ist einer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''library.lib'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
[move_z_to_x]&lt;br /&gt;
move_z_to_x:&lt;br /&gt;
_mov_lop:&lt;br /&gt;
   ld    r22, Z+                                            'read source&lt;br /&gt;
   st    x+, r22                                            'write target&lt;br /&gt;
   dec   r24                                                'length--&lt;br /&gt;
   brne  _mov_lop                                           '&amp;lt;&amp;gt; 0 ? --&amp;gt;loop&lt;br /&gt;
   ret&lt;br /&gt;
[end]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Autor==&lt;br /&gt;
*  [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Bascom]]&lt;br /&gt;
* [[Bascom Inside]]&lt;br /&gt;
[[Kategorie:Microcontroller]]&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Praxis]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom&amp;diff=14471</id>
		<title>Bascom</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom&amp;diff=14471"/>
				<updated>2009-01-07T17:10:15Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Wie man besonders kompakten Code erzeugt */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==AVR Bascom Basic==&lt;br /&gt;
&lt;br /&gt;
Bascom ist eine komplette Basic-Entwicklungsumgebung für die verschiedensten AVR Controller bzw. [[Controllerboard|Controllerboards]].Er bietet ein ungeheuer großes Leistungsvermögen und besonders anwenderfreundliche Entwicklungsumgebung. &lt;br /&gt;
Eine kostenlose Version, die bis zu 4 KB (das ist schon einiges bei einem Controller) keinerlei Einschränkungen besitzt, findet man auf der Seite des Herstellers &lt;br /&gt;
&lt;br /&gt;
[http://www.mcselec.com/index.php?option=com_docman&amp;amp;task=cat_view&amp;amp;gid=99&amp;amp;Itemid=54 Bascom-Download]&lt;br /&gt;
&lt;br /&gt;
Nach dem Download müssen alle Dateien entpackt und das SETUP-Programm aufgerufen werden. Danach steht ein Basic-Entwicklungssystem zur Verfügung, das alles beinhaltet was für die AVR-Programmierung notwendig ist. Zum Beispiel: Editor mit Befehlsvorschlag, Simulator, Terminalprogramm, Avr-FuseBit Einstellung, integrierter Assembler, eingebauter Programmer zur Übertragung des Programmcodes usw.&lt;br /&gt;
&lt;br /&gt;
Als erstes solltet ihr unter dem Menü Options / Compiler den Zielprozessor angeben. Fast alle gängigen AVR Controller können programmiert werden.&lt;br /&gt;
In diesem Dialog können auch noch viele weitere Einstellungen vorgenommen werden. Eigentlich ist das alles selbsterklärend. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
http://www.shop.robotikhardware.de/shop/catalog/images/artikelbilder/bascom/bascom.gif&lt;br /&gt;
&lt;br /&gt;
==Befehlsübersicht von Bascom==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Verzweigungen und Strukturbefehle===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
IF, THEN, ELSE, ELSEIF, END IF, DO, LOOP, WHILE, WEND, UNTIL,&lt;br /&gt;
EXIT DO, EXIT WHILE, FOR, NEXT, TO,  STEP, EXIT FOR,&lt;br /&gt;
ON .. GOTO/GOSUB, SELECT, CASE.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Ein- und Ausgabebefehle===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PRINT, INPUT, INKEY, PRINT, INPUTHEX, LCD, UPPERLINE, LOWERLINE,&lt;br /&gt;
DISPLAY ON/OFF, CURSOR ON/OFF/BLINK/NOBLINK, HOME, LOCATE, &lt;br /&gt;
SHIFTLCD LEFT/RIGHT, SHIFTCURSOR LEFT/RIGHT, CLS, DEFLCDCHAR, WAITKEY,&lt;br /&gt;
INPUTBIN, PRINTBIN,  OPEN, CLOSE, DEBOUNCE, SHIFTIN, SHIFTOUT,&lt;br /&gt;
GETATKBD, SPC, SERIN, SEROUT&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Mathematische Funktionen===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
AND, OR, XOR, INC, DEC, MOD, NOT, ABS, BCD, LOG, EXP, SQR, SIN,COS,&lt;br /&gt;
TAN, ATN, ATN2, ASIN, ACOS, FIX, ROUND, MOD, SGN, POWER, RAD2DEG,&lt;br /&gt;
DEG2RAD, LOG10, TANH, SINH, COSH.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===I2C-Bus===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
I2CSTART, I2CSTOP, I2CWBYTE, I2CRBYTE, I2CSEND, I2CRECEIVE.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===1WIRE Bus===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1WWRITE, 1WREAD, 1WRESET, 1WIRECOUNT, 1WSEARCHFIRST, 1WSEARCHNEXT.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===SPI-Bus===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SPIINIT, SPIIN, SPIOUT, SPIMOVE.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Datum und Zeitfunktionen===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DayOfWeek, DayOfYear, SecOfDay, SecElapsed, SysDay, SysSec, SysSecElapsed,&lt;br /&gt;
Time, Date, Time$, Date$&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Disk/Drive===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DriveReadSector, DriveWriteSector, DriveInit, DriveGetIdentity, &lt;br /&gt;
DriveReset, DriveCheck.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Dateisystem Befehle===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
InitFileSystem, DiskSize, DiskFree, Kill, Dir, Name, ChDir, MkDir, RmDir&lt;br /&gt;
FileLen, FileDateTime, FileDate, FileTime, GetAttr&lt;br /&gt;
FreeFile, Open, Close, Flush, Print, Write, Input, Line Input, Get, Put, Seek&lt;br /&gt;
EOF, LOC, LOF, FileAttr&lt;br /&gt;
BLoad, BSave&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Interrupt Programmierung===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ON INT0/INT1/TIMER0/TIMER1/SERIAL, RETURN, ENABLE, DISABLE, &lt;br /&gt;
COUNTERx, CAPTUREx, INTERRUPTS, CONFIG, START, LOAD.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Bit Manipulation===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SET, RESET, ROTATE, SHIFT, BITWAIT, TOGGLE.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Variablen===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DIM, BIT , BYTE , INTEGER , WORD, LONG, SINGLE, DOUBLE, STRING , DEFBIT,&lt;br /&gt;
DEFBYTE, DEFINT, DEFWORD.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sonstiges===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
REM, ' , SWAP, END, STOP, CONST, DELAY, WAIT, WAITMS, GOTO, GOSUB, &lt;br /&gt;
POWERDOWN, IDLE, DECLARE, CALL, SUB, END SUB, MAKEDEC, MAKEBCD, &lt;br /&gt;
INP,OUT, ALIAS, DIM , ERASE, DATA, READ, RESTORE, INCR, DECR, PEEK,&lt;br /&gt;
POKE, CPEEK, FUNCTION, READMAGCARD, BIN2GRAY, GRAY2BIN, CRC8, CRC16,&lt;br /&gt;
CHECKSUM, DTMFOUT, DTMFCODE.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Compiler Direktiven===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$INCLUDE, $BAUD and $CRYSTAL,  $SERIALINPUT, $SERIALOUTPUT, $RAMSIZE,&lt;br /&gt;
$RAMSTART,   $DEFAULT XRAM, $ASM-$END ASM, $LCD, $EXTERNAL, $LIB.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===String Manipulationen===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
STRING, SPACE, LEFT, RIGHT, MID, VAL, HEXVAL, LEN, STR, HEX, &lt;br /&gt;
LTRIM, RTRIM, TRIM, LCASE, UCASE, FORMAT, FUSING, INSTR. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Erläuterung grundlegender Bascom-Funktionen==&lt;br /&gt;
Hier einige kleine Programme, welche die grundlegende Funktionen und Syntax zeigen. Es wird immer ein komplettes Programm gezeigt, so dass man dieses einfach kopieren und austesten kann. Bascom Programme bestehen in der Regel fast immer nur aus einer Sourcecode-Datei (obwohl es auch anders geht), also nicht wie bei der Programmiersprache C üblich aus mehreren Dateien (Header-Dateien etc.). Auch das macht Bascom besonders einsteigerfreundlich.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Das berühmte Hello World Programm in Bascom===&lt;br /&gt;
Da bei einem Controllerboard gewöhnlich kein Bildschirm zur Verfügung steht, verbindet man das Board über einfaches RS232-Kabel (nur 3 Drähte sind notwendig) mit dem PC. Ein [[Terminalprogramm]] (Bascom hat bereits eines in der Entwicklungsumgebung integriert) zeigt nun auf dem Bildschirm alle Ausgaben des Boards (z.B. des Print-Befehles) an. Eine Methode, die zum Debuggen von Programmen oft genutzt wird. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $regfile = &amp;quot;m32def.dat&amp;quot; 'Die Anweisung bestimmt Controllertyp, hier AVR Mega 32&lt;br /&gt;
 $framesize = 32         'Stackanweisungen, die eigentlich nur bei größeren Programmen &lt;br /&gt;
 $swstack = 32           'wirklich nötig werden&lt;br /&gt;
 $hwstack = 32&lt;br /&gt;
 $crystal = 16000000     'Die Frequenz des verwendeten Quarzes&lt;br /&gt;
&lt;br /&gt;
 $baud = 9600            'Die Baudrate für RS232 Ausgabe. &lt;br /&gt;
                         'Sie muss auch bei PC Terminalprogramm identisch sein&lt;br /&gt;
  do&lt;br /&gt;
    Print &amp;quot;**** RN-CONTROL sagt Hello World *****&amp;quot;&lt;br /&gt;
    wait 1&lt;br /&gt;
  loop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Einen I/O Port umschalten===&lt;br /&gt;
Die wohl wichtigste Aufgabe beim Controller ist die Programmierung der Ports. Also das Bestimmen des Ausgangspegels eines Controllerpins. &lt;br /&gt;
Bascom verfügt über zwei Möglichkeiten die Ports zu beeinflussen, die sogenannten High-Level Befehle und die Registerzuweisungen wie sie in C üblich sind.&lt;br /&gt;
Zuerst ein Programm mit High-Level Anweisung. Ein [[Pin]] wird als Ausgang definiert und dann fortlaufend ein- und ausgeschaltet. Ist über einen Widerstand eine LED an diesem Controllerpin angeschlossen (so im Falle von [[RN-Control]]), so ergibt sich ein Blinken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $regfile = &amp;quot;m32def.dat&amp;quot;  'Die Anweisung bestimmt Controllertyp, hier AVR Mega 32&lt;br /&gt;
 $framesize = 32          'Stackanweisungen, die eigentlich nur bei größeren Programmen &lt;br /&gt;
 $swstack = 32            'wirklich nötig werden&lt;br /&gt;
 $hwstack = 32&lt;br /&gt;
 $crystal = 16000000      'Die Frequenz des verwendeten Quarzes&lt;br /&gt;
&lt;br /&gt;
 $baud = 9600             'Die Baudrate für RS232 Ausgabe. &lt;br /&gt;
                          'Sie muss auch bei PC Terminalprogramm identisch sein&lt;br /&gt;
&lt;br /&gt;
 Config Pinc.0 = Output  'Ein Pin wird als Ausgang konfiguriert PC0 (also Pin0 von Port C)&lt;br /&gt;
&lt;br /&gt;
  do&lt;br /&gt;
     Portc.0 = 1          'Pin wird auf High, also 5V geschaltet&lt;br /&gt;
     Waitms 100&lt;br /&gt;
     Portc.0 = 0          'Pin wird auf Low, also 0V geschaltet&lt;br /&gt;
     Waitms 100&lt;br /&gt;
  loop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man kann das Ganze noch etwas übersichtlicher gestalten, indem man dem Port-[[Pin]] im Programmcode eine andere Bezeichnung gibt, z.B. LED, wie in dem folgenden Beispiel. Durch den Alias-Befehl wird das ganze Programm wesentlich klarer, so dass einige Kommentarzeilen durchaus entfallen könnten. Man sollte sich daher angewöhnen, den Alias-Befehl auch zu nutzen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $regfile = &amp;quot;m32def.dat&amp;quot;  'Die Anweisung bestimmt Controllertyp, hier AVR Mega 32&lt;br /&gt;
 $framesize = 32          'Stackanweisungen, die eigentlich nur bei größeren Programmen &lt;br /&gt;
 $swstack = 32            'wirklich nötig werden&lt;br /&gt;
 $hwstack = 32&lt;br /&gt;
 $crystal = 16000000      'Die Frequenz des verwendeten Quarzes&lt;br /&gt;
&lt;br /&gt;
 $baud = 9600             'Die Baudrate für RS232 Ausgabe. &lt;br /&gt;
                          'Sie muss auch beim PC Terminalprogramm identisch sein&lt;br /&gt;
&lt;br /&gt;
 Config Pinc.0 = Output  'Ein Pin wird als Ausgang konfiguriert PC0 (also Pin0 von Port C)&lt;br /&gt;
 Led Alias Portc.0       &lt;br /&gt;
&lt;br /&gt;
  do&lt;br /&gt;
     Led = 1              'Pin wird auf High, also 5V geschaltet&lt;br /&gt;
     Waitms 100&lt;br /&gt;
     Led = 0              'Pin wird auf Low, also 0V geschaltet&lt;br /&gt;
     Waitms 100&lt;br /&gt;
  loop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Beispiel kommt ohne den High-Level Befehl CONFIG aus, indem direkt in das Datenrichtungsregister des Controllers geschrieben wird. In diesem Register steht jedes Bit für den Betriebsmodus eines Pins. Die 1 bedeutet, der Pin ist auf Ausgang geschaltet, 0 bedeutet Eingang. Diese Schreibweise hat den Nachteil, dass sie ein wenig unübersichtlicher ist, man kann sehr schnell [[Pin]]s verwechseln. Vorteil ist allerdings, dass alle 8 [[Pin]]s eines Ports gleichzeitig mit einer Zuweisung definiert werden können. Es gibt daher Programmierer, die diese Methode bevorzugen, insbesondere wenn sie auch Controller in C programmieren. Das Ergebnis ist in jedem Fall gleich: ein Blinklicht&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $regfile = &amp;quot;m32def.dat&amp;quot;  'Die Anweisung bestimmt Controllertyp, hier [[AVR]] Mega 32&lt;br /&gt;
 $framesize = 32          'Stackanweisungen, die eigentlich nur bei größeren Programmen &lt;br /&gt;
 $swstack = 32            'wirklich nötig werden&lt;br /&gt;
 $hwstack = 32&lt;br /&gt;
 $crystal = 16000000      'Die Frequenz des verwendeten Quarzes&lt;br /&gt;
&lt;br /&gt;
 $baud = 9600             'Die Baudrate für RS232 Ausgabe. &lt;br /&gt;
                          'Sie muss auch bei PC Terminalprogramm identisch sein&lt;br /&gt;
&lt;br /&gt;
 DDRC = &amp;amp;b00000001        'Port PC0 wird als Ausgang definiert, man hätte hier auch&lt;br /&gt;
                          'DDRC =1 schreiben können. Man verwendet aber oft die Bitdarstellung&lt;br /&gt;
                          'um alle 8 Bit besser überschauen zu können&lt;br /&gt;
&lt;br /&gt;
  do&lt;br /&gt;
     Portc.0 = 1          'Pin wird auf High, also 5V geschaltet&lt;br /&gt;
     Waitms 100&lt;br /&gt;
     Portc.0 = 0          'Pin wird auf Low, also 0V geschaltet&lt;br /&gt;
     Waitms 100&lt;br /&gt;
  loop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===I/O-Port als Eingang===&lt;br /&gt;
Im nachfolgenden Beispiel möchten wir ein PIN als Eingang verwenden und den Zustand eines Tasters abfragen. Dazu werden die Pole eines Tasters einmal mit GND (Masse) und einmal mit einem Port PA0 verbunden. Wir hätten auch jeden anderen Port nehmen können, da beim [[Avr]]-Controller nahezu alle Pins auch auf Eingang geschaltet werden können. Der Übersichtlichkeit halber verwenden wir wieder den Config- und den Alias-Befehl, um den Port in Taster umzutaufen.&lt;br /&gt;
Etwas gewöhnungsbedürftig ist in Bascom, dass man bei der Definition von Eingangsports nicht PORT sondern PIN beim Config-Befehl angibt. Eine weitere Besonderheit dieses Beispiels ist der Befehl ''Porta.0=1'' beim Eingabeport. Dieser Befehl sorgt dafür, dass im Controller der Eingangsport über einen hohen Widerstand (ca. 100k) mit High (5V) verbunden wird. Dadurch erreicht man, dass bei unbelegtem Port, in unserem Fall ungedrückte Taste, immer ein High Signal gelesen wird. Erst wenn der Taster gedrückt wird, wird diese Spannung quasi kurzgeschlossen und so ein LOW angelegt. Das ganze Beispiel bewirkt nun, dass bei gedrückter Taste die LED leuchtet und beim Loslassen wieder aus geht. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $regfile = &amp;quot;m32def.dat&amp;quot;  'Die Anweisung bestimmt Controllertyp, hier AVR Mega 32&lt;br /&gt;
 $framesize = 32          'Stackanweisungen, die eigentlich nur bei größeren Programmen &lt;br /&gt;
 $swstack = 32            'wirklich nötig werden&lt;br /&gt;
 $hwstack = 32&lt;br /&gt;
 $crystal = 16000000      'Die Frequenz des verwendeten Quarzes&lt;br /&gt;
&lt;br /&gt;
 $baud = 9600             'Die Baudrate für RS232 Ausgabe. &lt;br /&gt;
                          'Sie muss auch bei PC Terminalprogramm identisch sein&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Config Portc.0 = Output  'Ein Pin wird aus Ausgang konfiguriert PC0 (also Pin0 von Port C)&lt;br /&gt;
 Led Alias Portc.0       &lt;br /&gt;
 Config Pina.0 = Input    'Ein Pin (PA0) wird als Eingang definiert&lt;br /&gt;
 Taster Alias Pina.0&lt;br /&gt;
 Porta.0=1                'Interner Pullup Widerstand ein&lt;br /&gt;
 &lt;br /&gt;
  do&lt;br /&gt;
     if taster=0 then&lt;br /&gt;
       Led=1            'Pin wird auf High, also 5V geschaltet&lt;br /&gt;
     else&lt;br /&gt;
       Led = 0          'Pin wird auf Low, also 0V geschaltet&lt;br /&gt;
     endif&lt;br /&gt;
     Waitms 100&lt;br /&gt;
  loop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Tips und Tricks==&lt;br /&gt;
&lt;br /&gt;
===Wie man besonders kompakten Code erzeugt===&lt;br /&gt;
* Beachte das 32Byte HW-Stack-Minimum für ISR. &lt;br /&gt;
* Vermeide LOCAL Variable &lt;br /&gt;
* Vermeide SUB / FUNCTION mit Parameterübergabe &lt;br /&gt;
* Vermeide Bit-Variable &lt;br /&gt;
* Vermeide a&amp;gt;b, verwende a&amp;gt;=c oder b&amp;lt;a (RISC-Prozessor kennt kein größer als) &lt;br /&gt;
* Vermeide Double/Single/Long etc. und dazugehörige Matheoperationen (am besten nur Byte und Word)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Bit-Schreibweise mit Trennzeichen &amp;amp;B110_0111 ===&lt;br /&gt;
&lt;br /&gt;
Da sich die Verwendung des Unterstriches schwer erklären lässt hier ein Beispiel&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Const A = &amp;amp;B110_0111&lt;br /&gt;
&lt;br /&gt;
I = A&lt;br /&gt;
Print I ; &amp;quot;  &amp;quot; ; A&lt;br /&gt;
&lt;br /&gt;
Restore Test&lt;br /&gt;
Read I&lt;br /&gt;
Read J&lt;br /&gt;
Print I ; &amp;quot;   &amp;quot; ; J&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
Test:&lt;br /&gt;
Data &amp;amp;B1100111&lt;br /&gt;
Data &amp;amp;B110_0111&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und hier die Ausgabe im Simulator:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
103 &amp;amp;B1100111&lt;br /&gt;
103 6&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
man beachte den entfallenen Unterstrich von Print A&lt;br /&gt;
&lt;br /&gt;
=== Bit-Schreibweise in ASM ===&lt;br /&gt;
im Handbuch steht: &lt;br /&gt;
&amp;lt;pre&amp;gt;To refer to the bit number you must precede the variable name by BIT.&lt;br /&gt;
Sbrs R16 , BIT.B&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
es geht auch&amp;lt;pre&amp;gt;&lt;br /&gt;
ldi R24, 2^7+2^4+3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Bascom - Erstes Programm in den AVR Controller übertragen]]&lt;br /&gt;
* [[RN-Control]]&lt;br /&gt;
* [[RNBFRA-Board]]&lt;br /&gt;
* [[Avr]]&lt;br /&gt;
* [[RN-Board FAQ-Seite]] mit wichtigen Einstiegstips&lt;br /&gt;
* [[Sourcevergleich]] - GCC und Bascom&lt;br /&gt;
* [[Bascom State Machine Menu]] Umfangreiche Menüs in Bascom mit einer State Machine programmieren&lt;br /&gt;
* [[Bascom Inside]]&lt;br /&gt;
* [[Bascom Libraries]]&lt;br /&gt;
* [[Assembler Einführung für Bascom-User]]&lt;br /&gt;
* [[:Kategorie:Quellcode Bascom]]&lt;br /&gt;
&lt;br /&gt;
==Literatur==&lt;br /&gt;
* [[Buchvorstellungen|Programmieren der AVR RISC Mikrocontroller mit BASCOM-AVR; 2. Auflage]]&lt;br /&gt;
* [[Buchvorstellungen|Bascom–AVR, Autor M.Meissner - Beschreibung der Bascom IDE]]&lt;br /&gt;
* [[Buchvorstellungen|AVR-Microcontroller Lehrbuch – Ein tieferer Einstieg in Bascom und AT-MEGA8 und ähnliche AVR-Controller]]&lt;br /&gt;
* [[Buchvorstellungen|BASCOM-AVR Sprachbefehle - Ein umfangreiches Werk welches alle Befehle beschreibt ]]&lt;br /&gt;
* [[Buchvorstellungen|Mega16 Programmierung am Beispiel des RNBFRA-Boards]]&lt;br /&gt;
* [[Buchvorstellungen|Roboter selbst bauen von Ulli Sommer]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
* [http://www.mcselec.com Niederländischer Hersteller MCSELEC]&lt;br /&gt;
* [http://www.robotikhardware.de Bezugsquelle in Deutschland u.a. Robotikhardware.de] &lt;br /&gt;
* [http://www.roboternetz.de/phpBB2/viewtopic.php?t=1511 Bauanleitungen zu Experimentier- und Roboterboards]&lt;br /&gt;
* [http://www.roboternetz.de/phpBB2/dload.php?action=file&amp;amp;file_id=169 RN-Timer Windows Programm zur Timer-Berechnung]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Microcontroller]]&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:Robotikeinstieg]]&lt;br /&gt;
[[Kategorie:Praxis]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=14372</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=14372"/>
				<updated>2008-12-23T19:00:14Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Die Hardware für die Code-Beispiele: Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet noch kein Assembler und dient als Vorlage für Beispiel 2.&lt;br /&gt;
&lt;br /&gt;
Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet. Der Code ist bewusst simpel gestrickt. Trotzdem wurden bewusst mächtige Bascom Befehle wie &amp;quot;Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$&amp;quot; verwendet, um die Stärke vom Bascom gegenüber ASM herauszustellen.&lt;br /&gt;
&lt;br /&gt;
Die Uhr wird über 3 Tasten (Plus, Minus, Enter) gestellt. Jeder kennt das...&lt;br /&gt;
Beim Stellen laufen die Werte in beide Richtungen über (Beispiel Stunde : ...23,0,1....23,0,1..)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits etwas auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* der Befehl DEBONCE muss ständig gepollt werden, d.h. '''der AVR ist zu 100% beschäftigt (Batteriebetrieb!''')&lt;br /&gt;
* wird eine Taste gedrückt, '''friert der AVR für 30ms ein''' (andere Unterprogramme können in der Zeit nicht angesprungen werden)&lt;br /&gt;
* der Bascom Befehl Debounce kann '''kein Autorepeat''', d.h. langes Drücken einer Taste bewirkt keine zusätzlichen Impulse&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR verbringt über '''99,9% der Zeit im Powersave-Modus''' (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein '''Autorepeat''', d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
* kein zusätzlicher Timer erforderlich - das Unterprogramm Debounce_port wird '''an einem vorhandenen Timer rangehangen'''&lt;br /&gt;
* Bis zu '''8 Tasten können gleichzeitig (parallel) abgearbeitet''' werden.  Diese müssen aber am gleichen Port hängen. Falls das nicht möglich ist, muss das UP Debounce_port geringfügig erweitert werden, so dass am Anfang bis zu 8 Pins gesammelt und in einem Register zusammengeführt werden.&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   Bit des Pins wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Die Tasten werden im Beispiel mit 128Hz abgetastet. Eine Taste muss 3x in Folge den gleichen Pegelk haben, um als Zustandswechsel anerkannt zu werden. (Verzögerung ca. 30ms).&lt;br /&gt;
 &lt;br /&gt;
Wer es etwas ruhiger mag, kann wegen der vorgegebenen Prescaler von Timer2 (1,8,32,64 etc) erst 16 Hz als nächsten Takt zum Pollen wählen. Das ergibt unangenehme Verzögerungen beim Tastendruck von ca. 0,2s.&lt;br /&gt;
&lt;br /&gt;
Andere Frequenzen erreicht man aber durch den CTC Mode. Der wird von Bascom nicht unterstützt, weshalb man die Register von Hand setzen muss. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
'...but if you love 32hz debounce use CTC (max. Verzögerung 4/32=0,125s)&lt;br /&gt;
  ' Tccr2 = &amp;amp;B0000_1010                                         'CTC und Prescale=8 @32768Hz&lt;br /&gt;
  ' Assr = &amp;amp;B0000_1000                                          'clocked from crystal Oscillator&lt;br /&gt;
  ' Ocr2 = 127                                                  '32768/(8*(127+1))=32Hz&lt;br /&gt;
  ' On Oc2 _isr_t2ovf Nosave                                   'Interrupt NOSAVE_ISR!&lt;br /&gt;
  ' Enable Oc2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in der ISR ist dann die folgende Zeile der Frequenz entsprechend anzupassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Warum eine ISR in Assembler programmieren?==&lt;br /&gt;
Ein gutes AVR-Programm hängt nicht irgendwo im Code fest. &lt;br /&gt;
Insbesonders sollten ISR sehr schnell durchlaufen werden, um andere Prozesse nicht zum Stocken zu bringen. (Infrarotsensor auslesen etc). &lt;br /&gt;
&lt;br /&gt;
Stehen nach einem Interrupt größere Sachen an (Print, LCD etc), dann setzt man in der ISR nur ein Flag und bearbeitet das Problem in der Main. Im Beispiel 2 wurde das Flag flagRTC_Changed gesetzt, um jede Sekunde das LCD zu aktualisieren. In der Variablen Key_press wird nach jedem Interrupt-Ereignis (Aufwachen aus Powersave) nachgesehen, ob eine neue Taste erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
Wird in Bascom eine ISR ohne dem Zusatz NOSAVE programmiert, sichert Bascom fast alle Register auf den Stack. Das kostet Zeit und führt zu Stacküberläufen, wenn man den Stackbereich nicht deutlich anhebt. Hier ein Bascom-Code Schnipsel einer solchen ISR.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 PUSH    R0               Push register on stack&lt;br /&gt;
 PUSH    R1 &lt;br /&gt;
 PUSH    R2 &lt;br /&gt;
 PUSH    R3 &lt;br /&gt;
 PUSH    R4 &lt;br /&gt;
 PUSH    R5 &lt;br /&gt;
 PUSH    R7 &lt;br /&gt;
 PUSH    R10&lt;br /&gt;
 PUSH    R11&lt;br /&gt;
 PUSH    R16&lt;br /&gt;
 PUSH    R17&lt;br /&gt;
 PUSH    R18&lt;br /&gt;
 PUSH    R19&lt;br /&gt;
 PUSH    R20&lt;br /&gt;
 PUSH    R21&lt;br /&gt;
 PUSH    R22&lt;br /&gt;
 PUSH    R23&lt;br /&gt;
 PUSH    R24&lt;br /&gt;
 PUSH    R25&lt;br /&gt;
 PUSH    R26&lt;br /&gt;
 PUSH    R27&lt;br /&gt;
 PUSH    R28&lt;br /&gt;
 PUSH    R29&lt;br /&gt;
 PUSH    R30&lt;br /&gt;
 PUSH    R31&lt;br /&gt;
&lt;br /&gt;
 IN      R24,SREG         In from I/O location&lt;br /&gt;
 PUSH    R24              Push register on stack&lt;br /&gt;
&lt;br /&gt;
'eigener Code in der ISR&lt;br /&gt;
&lt;br /&gt;
 POP     R24              Pop register from stack&lt;br /&gt;
 OUT     SREG,R24         Out to I/O location&lt;br /&gt;
&lt;br /&gt;
 POP     R31              Pop register from stack&lt;br /&gt;
 POP     R30&lt;br /&gt;
 POP     R29&lt;br /&gt;
 POP     R28&lt;br /&gt;
 POP     R27&lt;br /&gt;
 POP     R26&lt;br /&gt;
 POP     R25&lt;br /&gt;
 POP     R24&lt;br /&gt;
 POP     R23&lt;br /&gt;
 POP     R22&lt;br /&gt;
 POP     R21&lt;br /&gt;
 POP     R20&lt;br /&gt;
 POP     R19&lt;br /&gt;
 POP     R18&lt;br /&gt;
 POP     R17&lt;br /&gt;
 POP     R16&lt;br /&gt;
 POP     R11&lt;br /&gt;
 POP     R10&lt;br /&gt;
 POP     R7 &lt;br /&gt;
 POP     R5 &lt;br /&gt;
 POP     R4 &lt;br /&gt;
 POP     R3 &lt;br /&gt;
 POP     R2 &lt;br /&gt;
 POP     R1 &lt;br /&gt;
 POP     R0 &lt;br /&gt;
 RETI  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das sind 26 zusätzliche Byte auf dem Stack. Unser Beispiel 2 benötigt nur 4 Register und SREG, d.h. 5 Byte auf dem Stack.&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13964</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13964"/>
				<updated>2008-09-03T23:25:24Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Warum eine ISR in Assembler programmieren? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Die Hardware für die Code-Beispiele: Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet noch kein Assembler und dient als Vorlage für Beispiel 2.&lt;br /&gt;
&lt;br /&gt;
Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet. Der Code ist bewusst simpel gestrickt. Trotzdem wurden bewusst mächtige Bascom Befehle wie &amp;quot;Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$&amp;quot; verwendet, um die Stärke vom Bascom gegenüber ASM herauszustellen.&lt;br /&gt;
&lt;br /&gt;
Die Uhr wird über 3 Tasten (Plus, Minus, Enter) gestellt. Jeder kennt das...&lt;br /&gt;
Beim Stellen laufen die Werte in beide Richtungen über (Beispiel Stunde : ...23,0,1....23,0,1..)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits etwas auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* der Befehl DEBONCE muss ständig gepollt werden, d.h. '''der AVR ist zu 100% beschäftigt (Batteriebetrieb!''')&lt;br /&gt;
* wird eine Taste gedrückt, '''friert der AVR für 30ms ein''' (andere Unterprogramme können in der Zeit nicht angesprungen werden)&lt;br /&gt;
* der Bascom Befehl Debounce kann '''kein Autorepeat''', d.h. langes Drücken einer Taste bewirkt keine zusätzlichen Impulse&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR verbringt über '''99,9% der Zeit im Powersave-Modus''' (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein '''Autorepeat''', d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
* kein zusätzlicher Timer erforderlich - das Unterprogramm Debounce_port wird '''an einem vorhandenen Timer rangehangen'''&lt;br /&gt;
* Bis zu '''8 Tasten können gleichzeitig (parallel) abgearbeitet''' werden.  Diese müssen aber am gleichen Port hängen. Falls das nicht möglich ist, muss das UP Debounce_port geringfügig erweitert werden, so dass am Anfang bis zu 8 Pins gesammelt und in einem Register zusammengeführt werden.&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   Bit des Pins wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Die Tasten werden im Beispiel mit 128Hz abgetastet. Eine Taste muss 3x in Folge den gleichen Pegelk haben, um als Zustandswechsel anerkannt zu werden. (Verzögerung ca. 30ms).&lt;br /&gt;
 &lt;br /&gt;
Wer es etwas ruhiger mag, kann wegen der vorgegebenen Prescaler von Timer2 (1,8,32,64 etc) erst 16 Hz als nächsten Takt zum Pollen wählen. Das ergibt unangenehme Verzögerungen beim Tastendruck von ca. 0,2s.&lt;br /&gt;
&lt;br /&gt;
Andere Frequenzen erreicht man aber durch den CTC Mode. Der wird von Bascom nicht unterstützt, weshalb man die Register von Hand setzen muss. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
'...but if you love 32hz debounce use CTC (max. Verzögerung 4/32=0,125s)&lt;br /&gt;
  ' Tccr2 = &amp;amp;B0000_1010                                         'CTC und Prescale=8 @32768Hz&lt;br /&gt;
  ' Assr = &amp;amp;B0000_1000                                          'clocked from crystal Oscillator&lt;br /&gt;
  ' Ocr2 = 127                                                  '32768/(8*(127+1))=32Hz&lt;br /&gt;
  ' On Oc2 _isr_t2ovf Nosave                                   'Interrupt NOSAVE_ISR!&lt;br /&gt;
  ' Enable Oc2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in der ISR ist dann die folgende Zeile der Frequenz entsprechend anzupassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Warum eine ISR in Assembler programmieren?==&lt;br /&gt;
Ein gutes AVR-Programm hängt nicht irgendwo im Code fest. &lt;br /&gt;
Insbesonders sollten ISR sehr schnell durchlaufen werden, um andere Prozesse nicht zum Stocken zu bringen. (Infrarotsensor auslesen etc). &lt;br /&gt;
&lt;br /&gt;
Stehen nach einem Interrupt größere Sachen an (Print, LCD etc), dann setzt man in der ISR nur ein Flag und bearbeitet das Problem in der Main. Im Beispiel 2 wurde das Flag flagRTC_Changed gesetzt, um jede Sekunde das LCD zu aktualisieren. In der Variablen Key_press wird nach jedem Interrupt-Ereignis (Aufwachen aus Powersave) nachgesehen, ob eine neue Taste erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
Wird in Bascom eine ISR ohne dem Zusatz NOSAVE programmiert, sichert Bascom fast alle Register auf den Stack. Das kostet Zeit und führt zu Stacküberläufen, wenn man den Stackbereich nicht deutlich anhebt. Hier ein Bascom-Code Schnipsel einer solchen ISR.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 PUSH    R0               Push register on stack&lt;br /&gt;
 PUSH    R1 &lt;br /&gt;
 PUSH    R2 &lt;br /&gt;
 PUSH    R3 &lt;br /&gt;
 PUSH    R4 &lt;br /&gt;
 PUSH    R5 &lt;br /&gt;
 PUSH    R7 &lt;br /&gt;
 PUSH    R10&lt;br /&gt;
 PUSH    R11&lt;br /&gt;
 PUSH    R16&lt;br /&gt;
 PUSH    R17&lt;br /&gt;
 PUSH    R18&lt;br /&gt;
 PUSH    R19&lt;br /&gt;
 PUSH    R20&lt;br /&gt;
 PUSH    R21&lt;br /&gt;
 PUSH    R22&lt;br /&gt;
 PUSH    R23&lt;br /&gt;
 PUSH    R24&lt;br /&gt;
 PUSH    R25&lt;br /&gt;
 PUSH    R26&lt;br /&gt;
 PUSH    R27&lt;br /&gt;
 PUSH    R28&lt;br /&gt;
 PUSH    R29&lt;br /&gt;
 PUSH    R30&lt;br /&gt;
 PUSH    R31&lt;br /&gt;
&lt;br /&gt;
 IN      R24,SREG         In from I/O location&lt;br /&gt;
 PUSH    R24              Push register on stack&lt;br /&gt;
&lt;br /&gt;
'eigener Code in der ISR&lt;br /&gt;
&lt;br /&gt;
 POP     R24              Pop register from stack&lt;br /&gt;
 OUT     SREG,R24         Out to I/O location&lt;br /&gt;
&lt;br /&gt;
 POP     R31              Pop register from stack&lt;br /&gt;
 POP     R30&lt;br /&gt;
 POP     R29&lt;br /&gt;
 POP     R28&lt;br /&gt;
 POP     R27&lt;br /&gt;
 POP     R26&lt;br /&gt;
 POP     R25&lt;br /&gt;
 POP     R24&lt;br /&gt;
 POP     R23&lt;br /&gt;
 POP     R22&lt;br /&gt;
 POP     R21&lt;br /&gt;
 POP     R20&lt;br /&gt;
 POP     R19&lt;br /&gt;
 POP     R18&lt;br /&gt;
 POP     R17&lt;br /&gt;
 POP     R16&lt;br /&gt;
 POP     R11&lt;br /&gt;
 POP     R10&lt;br /&gt;
 POP     R7 &lt;br /&gt;
 POP     R5 &lt;br /&gt;
 POP     R4 &lt;br /&gt;
 POP     R3 &lt;br /&gt;
 POP     R2 &lt;br /&gt;
 POP     R1 &lt;br /&gt;
 POP     R0 &lt;br /&gt;
 RETI  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das sind 26 zusätzliche Byte auf dem Stack. Unser Beispiel 2 benötigt nur 4 Register und SREG, d.h. 5 Byte auf dem Stack.&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13963</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13963"/>
				<updated>2008-09-03T23:24:10Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Die Hardware für die Code-Beispiele: Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet noch kein Assembler und dient als Vorlage für Beispiel 2.&lt;br /&gt;
&lt;br /&gt;
Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet. Der Code ist bewusst simpel gestrickt. Trotzdem wurden bewusst mächtige Bascom Befehle wie &amp;quot;Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$&amp;quot; verwendet, um die Stärke vom Bascom gegenüber ASM herauszustellen.&lt;br /&gt;
&lt;br /&gt;
Die Uhr wird über 3 Tasten (Plus, Minus, Enter) gestellt. Jeder kennt das...&lt;br /&gt;
Beim Stellen laufen die Werte in beide Richtungen über (Beispiel Stunde : ...23,0,1....23,0,1..)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits etwas auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* der Befehl DEBONCE muss ständig gepollt werden, d.h. '''der AVR ist zu 100% beschäftigt (Batteriebetrieb!''')&lt;br /&gt;
* wird eine Taste gedrückt, '''friert der AVR für 30ms ein''' (andere Unterprogramme können in der Zeit nicht angesprungen werden)&lt;br /&gt;
* der Bascom Befehl Debounce kann '''kein Autorepeat''', d.h. langes Drücken einer Taste bewirkt keine zusätzlichen Impulse&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR verbringt über '''99,9% der Zeit im Powersave-Modus''' (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein '''Autorepeat''', d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
* kein zusätzlicher Timer erforderlich - das Unterprogramm Debounce_port wird '''an einem vorhandenen Timer rangehangen'''&lt;br /&gt;
* Bis zu '''8 Tasten können gleichzeitig (parallel) abgearbeitet''' werden.  Diese müssen aber am gleichen Port hängen. Falls das nicht möglich ist, muss das UP Debounce_port geringfügig erweitert werden, so dass am Anfang bis zu 8 Pins gesammelt und in einem Register zusammengeführt werden.&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   Bit des Pins wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Die Tasten werden im Beispiel mit 128Hz abgetastet. Eine Taste muss 3x in Folge den gleichen Pegelk haben, um als Zustandswechsel anerkannt zu werden. (Verzögerung ca. 30ms).&lt;br /&gt;
 &lt;br /&gt;
Wer es etwas ruhiger mag, kann wegen der vorgegebenen Prescaler von Timer2 (1,8,32,64 etc) erst 16 Hz als nächsten Takt zum Pollen wählen. Das ergibt unangenehme Verzögerungen beim Tastendruck von ca. 0,2s.&lt;br /&gt;
&lt;br /&gt;
Andere Frequenzen erreicht man aber durch den CTC Mode. Der wird von Bascom nicht unterstützt, weshalb man die Register von Hand setzen muss. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
'...but if you love 32hz debounce use CTC (max. Verzögerung 4/32=0,125s)&lt;br /&gt;
  ' Tccr2 = &amp;amp;B0000_1010                                         'CTC und Prescale=8 @32768Hz&lt;br /&gt;
  ' Assr = &amp;amp;B0000_1000                                          'clocked from crystal Oscillator&lt;br /&gt;
  ' Ocr2 = 127                                                  '32768/(8*(127+1))=32Hz&lt;br /&gt;
  ' On Oc2 _isr_t2ovf Nosave                                   'Interrupt NOSAVE_ISR!&lt;br /&gt;
  ' Enable Oc2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in der ISR ist dann die folgende Zeile der Frequenz entsprechend anzupassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Warum eine ISR in Assembler programmieren?==&lt;br /&gt;
Ein gutes AVR-Programm hängt nicht irgendwo im Code fest. &lt;br /&gt;
Insbesonders sollten ISR sehr schnell durchlaufen werden, um andere Prozesse nicht zum Stocken zu bringen. (Infrarotsensor auslesen etc). &lt;br /&gt;
&lt;br /&gt;
Stehen nach einem Interrupt größere Sachen an (Print, LCD etc), dann setzt man in der ISR nur ein Flag und bearbeitet das Problem in der Main. Im Beispiel 2 wurde das Flag flagRTC_Changed gesetzt, um jede Sekunde das LCD zu aktualisieren. In der Variablen Key_press wird nach jedem Interrupt-Ereignis (Aufwachen aus Powersave) nachgesehen, ob eine neue Taste erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
Wird in Bascom eine ISR ohne dem Zusatz NOSAVE programmiert, sichert Bascom fast alle Register auf den Stack. Das kostet Zeit und führt zu Stacküberläufen, wenn man den Stackbereich nicht deutlich anhebt. Hier ein Bascom-Code Schnipsel einer solchen ISR.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 PUSH    R0               Push register on stack&lt;br /&gt;
 PUSH    R1 &lt;br /&gt;
 PUSH    R2 &lt;br /&gt;
 PUSH    R3 &lt;br /&gt;
 PUSH    R4 &lt;br /&gt;
 PUSH    R5 &lt;br /&gt;
 PUSH    R7 &lt;br /&gt;
 PUSH    R10&lt;br /&gt;
 PUSH    R11&lt;br /&gt;
 PUSH    R16&lt;br /&gt;
 PUSH    R17&lt;br /&gt;
 PUSH    R18&lt;br /&gt;
 PUSH    R19&lt;br /&gt;
 PUSH    R20&lt;br /&gt;
 PUSH    R21&lt;br /&gt;
 PUSH    R22&lt;br /&gt;
 PUSH    R23&lt;br /&gt;
 PUSH    R24&lt;br /&gt;
 PUSH    R25&lt;br /&gt;
 PUSH    R26&lt;br /&gt;
 PUSH    R27&lt;br /&gt;
 PUSH    R28&lt;br /&gt;
 PUSH    R29&lt;br /&gt;
 PUSH    R30&lt;br /&gt;
 PUSH    R31&lt;br /&gt;
&lt;br /&gt;
 IN      R24,SREG         In from I/O location&lt;br /&gt;
 PUSH    R24              Push register on stack&lt;br /&gt;
&lt;br /&gt;
'eigener Code in der ISR&lt;br /&gt;
&lt;br /&gt;
 POP     R24              Pop register from stack&lt;br /&gt;
 OUT     0x3F,SREG         Out to I/O location&lt;br /&gt;
&lt;br /&gt;
 POP     R31              Pop register from stack&lt;br /&gt;
 POP     R30&lt;br /&gt;
 POP     R29&lt;br /&gt;
 POP     R28&lt;br /&gt;
 POP     R27&lt;br /&gt;
 POP     R26&lt;br /&gt;
 POP     R25&lt;br /&gt;
 POP     R24&lt;br /&gt;
 POP     R23&lt;br /&gt;
 POP     R22&lt;br /&gt;
 POP     R21&lt;br /&gt;
 POP     R20&lt;br /&gt;
 POP     R19&lt;br /&gt;
 POP     R18&lt;br /&gt;
 POP     R17&lt;br /&gt;
 POP     R16&lt;br /&gt;
 POP     R11&lt;br /&gt;
 POP     R10&lt;br /&gt;
 POP     R7 &lt;br /&gt;
 POP     R5 &lt;br /&gt;
 POP     R4 &lt;br /&gt;
 POP     R3 &lt;br /&gt;
 POP     R2 &lt;br /&gt;
 POP     R1 &lt;br /&gt;
 POP     R0 &lt;br /&gt;
 RETI  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das sind 26 zusätzliche Byte auf dem Stack. Unser Beispiel 2 benötigt nur 4 Register und SREG, d.h. 5 Byte auf dem Stack.&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13962</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13962"/>
				<updated>2008-09-03T23:20:57Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Die Hardware für die Code-Beispiele: Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet noch kein Assembler und dient als Vorlage für Beispiel 2.&lt;br /&gt;
&lt;br /&gt;
Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet. Der Code ist bewusst simpel gestrickt. Trotzdem wurden bewusst mächtige Bascom Befehle wie &amp;quot;Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$&amp;quot; verwendet, um die Stärke vom Bascom gegenüber ASM herauszustellen.&lt;br /&gt;
&lt;br /&gt;
Die Uhr wird über 3 Tasten (Plus, Minus, Enter) gestellt. Jeder kennt das...&lt;br /&gt;
Beim Stellen laufen die Werte in beide Richtungen über (Beispiel Stunde : ...23,0,1....23,0,1..)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits etwas auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* der Befehl DEBONCE muss ständig gepollt werden, d.h. '''der AVR ist zu 100% beschäftigt (Batteriebetrieb!''')&lt;br /&gt;
* wird eine Taste gedrückt, '''friert der AVR für 30ms ein''' (andere Unterprogramme können in der Zeit nicht angesprungen werden)&lt;br /&gt;
* der Bascom Befehl Debounce kann '''kein Autorepeat''', d.h. langes Drücken einer Taste bewirkt keine zusätzlichen Impulse&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR verbringt über '''99,9% der Zeit im Powersave-Modus''' (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein '''Autorepeat''', d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
* kein zusätzlicher Timer erforderlich - das Unterprogramm Debounce wird an einem vorhandenen Timer rangehangen&lt;br /&gt;
* Bis zu 8 Tasten können gleichzeitig (parallel) abgearbeitet werden.  Diese müssen aber am gleichen Port hängen. Falls das nicht möglich ist, muss das UP Debounce_port geringfügig erweitert werden, so dass am Anfang bis zu 8 Pins gesammelt und in einem Register zusammengeführt werden.&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   Bit des Pins wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Die Tasten werden im Beispiel mit 128Hz abgetastet. Eine Taste muss 3x in Folge den gleichen Pegelk haben, um als Zustandswechsel anerkannt zu werden. (Verzögerung ca. 30ms).&lt;br /&gt;
 &lt;br /&gt;
Wer es etwas ruhiger mag, kann wegen der vorgegebenen Prescaler von Timer2 (1,8,32,64 etc) erst 16 Hz als nächsten Takt zum Pollen wählen. Das ergibt unangenehme Verzögerungen beim Tastendruck von ca. 0,2s.&lt;br /&gt;
&lt;br /&gt;
Andere Frequenzen erreicht man aber durch den CTC Mode. Der wird von Bascom nicht unterstützt, weshalb man die Register von Hand setzen muss. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
'...but if you love 32hz debounce use CTC (max. Verzögerung 4/32=0,125s)&lt;br /&gt;
  ' Tccr2 = &amp;amp;B0000_1010                                         'CTC und Prescale=8 @32768Hz&lt;br /&gt;
  ' Assr = &amp;amp;B0000_1000                                          'clocked from crystal Oscillator&lt;br /&gt;
  ' Ocr2 = 127                                                  '32768/(8*(127+1))=32Hz&lt;br /&gt;
  ' On Oc2 _isr_t2ovf Nosave                                   'Interrupt NOSAVE_ISR!&lt;br /&gt;
  ' Enable Oc2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in der ISR ist dann die folgende Zeile der Frequenz entsprechend anzupassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Warum eine ISR in Assembler programmieren?==&lt;br /&gt;
Ein gutes AVR-Programm hängt nicht irgendwo im Code fest. &lt;br /&gt;
Insbesonders sollten ISR sehr schnell durchlaufen werden, um andere Prozesse nicht zum Stocken zu bringen. (Infrarotsensor auslesen etc). &lt;br /&gt;
&lt;br /&gt;
Stehen nach einem Interrupt größere Sachen an (Print, LCD etc), dann setzt man in der ISR nur ein Flag und bearbeitet das Problem in der Main. Im Beispiel 2 wurde das Flag flagRTC_Changed gesetzt, um jede Sekunde das LCD zu aktualisieren. In der Variablen Key_press wird nach jedem Interrupt-Ereignis (Aufwachen aus Powersave) nachgesehen, ob eine neue Taste erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
Wird in Bascom eine ISR ohne dem Zusatz NOSAVE programmiert, sichert Bascom fast alle Register auf den Stack. Das kostet Zeit und führt zu Stacküberläufen, wenn man den Stackbereich nicht deutlich anhebt. Hier ein Bascom-Code Schnipsel einer solchen ISR.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 PUSH    R0               Push register on stack&lt;br /&gt;
 PUSH    R1 &lt;br /&gt;
 PUSH    R2 &lt;br /&gt;
 PUSH    R3 &lt;br /&gt;
 PUSH    R4 &lt;br /&gt;
 PUSH    R5 &lt;br /&gt;
 PUSH    R7 &lt;br /&gt;
 PUSH    R10&lt;br /&gt;
 PUSH    R11&lt;br /&gt;
 PUSH    R16&lt;br /&gt;
 PUSH    R17&lt;br /&gt;
 PUSH    R18&lt;br /&gt;
 PUSH    R19&lt;br /&gt;
 PUSH    R20&lt;br /&gt;
 PUSH    R21&lt;br /&gt;
 PUSH    R22&lt;br /&gt;
 PUSH    R23&lt;br /&gt;
 PUSH    R24&lt;br /&gt;
 PUSH    R25&lt;br /&gt;
 PUSH    R26&lt;br /&gt;
 PUSH    R27&lt;br /&gt;
 PUSH    R28&lt;br /&gt;
 PUSH    R29&lt;br /&gt;
 PUSH    R30&lt;br /&gt;
 PUSH    R31&lt;br /&gt;
&lt;br /&gt;
 IN      R24,SREG         In from I/O location&lt;br /&gt;
 PUSH    R24              Push register on stack&lt;br /&gt;
&lt;br /&gt;
'eigener Code in der ISR&lt;br /&gt;
&lt;br /&gt;
 POP     R24              Pop register from stack&lt;br /&gt;
 OUT     0x3F,SREG         Out to I/O location&lt;br /&gt;
&lt;br /&gt;
 POP     R31              Pop register from stack&lt;br /&gt;
 POP     R30&lt;br /&gt;
 POP     R29&lt;br /&gt;
 POP     R28&lt;br /&gt;
 POP     R27&lt;br /&gt;
 POP     R26&lt;br /&gt;
 POP     R25&lt;br /&gt;
 POP     R24&lt;br /&gt;
 POP     R23&lt;br /&gt;
 POP     R22&lt;br /&gt;
 POP     R21&lt;br /&gt;
 POP     R20&lt;br /&gt;
 POP     R19&lt;br /&gt;
 POP     R18&lt;br /&gt;
 POP     R17&lt;br /&gt;
 POP     R16&lt;br /&gt;
 POP     R11&lt;br /&gt;
 POP     R10&lt;br /&gt;
 POP     R7 &lt;br /&gt;
 POP     R5 &lt;br /&gt;
 POP     R4 &lt;br /&gt;
 POP     R3 &lt;br /&gt;
 POP     R2 &lt;br /&gt;
 POP     R1 &lt;br /&gt;
 POP     R0 &lt;br /&gt;
 RETI  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das sind 26 zusätzliche Byte auf dem Stack. Unser Beispiel 2 benötigt nur 4 Register und SREG, d.h. 5 Byte auf dem Stack.&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13961</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13961"/>
				<updated>2008-09-03T23:09:35Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Die Hardware für die Code-Beispiele: Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet noch kein Assembler und dient als Vorlage für Beispiel 2.&lt;br /&gt;
&lt;br /&gt;
Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet. Der Code ist bewusst simpel gestrickt. Trotzdem wurden bewusst mächtige Bascom Befehle wie &amp;quot;Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$&amp;quot; verwendet, um die Stärke vom Bascom gegenüber ASM herauszustellen.&lt;br /&gt;
&lt;br /&gt;
Die Uhr wird über 3 Tasten (Plus, Minus, Enter) gestellt. Jeder kennt das...&lt;br /&gt;
Beim Stellen laufen die Werte in beide Richtungen über (Beispiel Stunde : ...23,0,1....23,0,1..)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits etwas auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* der Befehl DEBONCE muss ständig gepollt werden, d.h. '''der AVR ist zu 100% beschäftigt (Batteriebetrieb!''')&lt;br /&gt;
* wird eine Taste gedrückt, '''friert der AVR für 30ms ein''' (andere Unterprogramme können in der Zeit nicht angesprungen werden)&lt;br /&gt;
* der Bascom Befehl Debounce kann '''kein Autorepeat''', d.h. langes Drücken einer Taste bewirkt keine zusätzlichen Impulse&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR verbringt über '''99,9% der Zeit im Powersave-Modus''' (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein '''Autorepeat''', d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
* kein zusätzlicher Timer erforderlich - das Unterprogramm Debounce wird an einem vorhandenen Timer rangehangen&lt;br /&gt;
* Bis zu 8 Tasten können gleichzeitig (parallel) abgearbeitet werden.  Diese müssen aber am gleichen Port hängen. Falls das nicht möglich ist, muss das UP Debounce_port geringfügig erweitert werden, so dass am Anfang bis zu 8 Pins gesammelt und in einem Register zusammengeführt werden.&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Die Tasten werden im Beispiel mit 128Hz abgetastet. Eine Taste muss 3x in Folge den gleichen Pegelk haben, um als Zustandswechsel anerkannt zu werden. (Verzögerung ca. 30ms).&lt;br /&gt;
 &lt;br /&gt;
Wer es etwas ruhiger mag, kann wegen der vorgegebenen Prescaler von Timer2 (1,8,32,64 etc) erst 16 Hz als nächsten Takt zum Pollen wählen. Das ergibt unangenehme Verzögerungen beim Tastendruck von ca. 0,2s.&lt;br /&gt;
&lt;br /&gt;
Andere Frequenzen erreicht man aber durch den CTC Mode. Der wird von Bascom nicht unterstützt, weshalb man die Register von Hand setzen muss. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
'...but if you love 32hz debounce use CTC (max. Verzögerung 4/32=0,125s)&lt;br /&gt;
  ' Tccr2 = &amp;amp;B0000_1010                                         'CTC und Prescale=8 @32768Hz&lt;br /&gt;
  ' Assr = &amp;amp;B0000_1000                                          'clocked from crystal Oscillator&lt;br /&gt;
  ' Ocr2 = 127                                                  '32768/(8*(127+1))=32Hz&lt;br /&gt;
  ' On Oc2 _isr_t2ovf Nosave                                   'Interrupt NOSAVE_ISR!&lt;br /&gt;
  ' Enable Oc2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in der ISR ist dann die folgende Zeile der Frequenz entsprechend anzupassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Warum eine ISR in Assembler programmieren?==&lt;br /&gt;
Ein gutes AVR-Programm hängt nicht irgendwo im Code fest. &lt;br /&gt;
Insbesonders sollten ISR sehr schnell durchlaufen werden, um andere Prozesse nicht zum Stocken zu bringen. (Infrarotsensor auslesen etc). &lt;br /&gt;
&lt;br /&gt;
Stehen nach einem Interrupt größere Sachen an (Print, LCD etc), dann setzt man in der ISR nur ein Flag und bearbeitet das Problem in der Main. Im Beispiel 2 wurde das Flag flagRTC_Changed gesetzt, um jede Sekunde das LCD zu aktualisieren. In der Variablen Key_press wird nach jedem Interrupt-Ereignis (Aufwachen aus Powersave) nachgesehen, ob eine neue Taste erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
Wird in Bascom eine ISR ohne dem Zusatz NOSAVE programmiert, sichert Bascom fast alle Register auf den Stack. Das kostet Zeit und führt zu Stacküberläufen, wenn man den Stackbereich nicht deutlich anhebt. Hier ein Bascom-Code Schnipsel einer solchen ISR.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 PUSH    R0               Push register on stack&lt;br /&gt;
 PUSH    R1 &lt;br /&gt;
 PUSH    R2 &lt;br /&gt;
 PUSH    R3 &lt;br /&gt;
 PUSH    R4 &lt;br /&gt;
 PUSH    R5 &lt;br /&gt;
 PUSH    R7 &lt;br /&gt;
 PUSH    R10&lt;br /&gt;
 PUSH    R11&lt;br /&gt;
 PUSH    R16&lt;br /&gt;
 PUSH    R17&lt;br /&gt;
 PUSH    R18&lt;br /&gt;
 PUSH    R19&lt;br /&gt;
 PUSH    R20&lt;br /&gt;
 PUSH    R21&lt;br /&gt;
 PUSH    R22&lt;br /&gt;
 PUSH    R23&lt;br /&gt;
 PUSH    R24&lt;br /&gt;
 PUSH    R25&lt;br /&gt;
 PUSH    R26&lt;br /&gt;
 PUSH    R27&lt;br /&gt;
 PUSH    R28&lt;br /&gt;
 PUSH    R29&lt;br /&gt;
 PUSH    R30&lt;br /&gt;
 PUSH    R31&lt;br /&gt;
&lt;br /&gt;
 IN      R24,SREG         In from I/O location&lt;br /&gt;
 PUSH    R24              Push register on stack&lt;br /&gt;
&lt;br /&gt;
'eigener Code in der ISR&lt;br /&gt;
&lt;br /&gt;
 POP     R24              Pop register from stack&lt;br /&gt;
 OUT     0x3F,SREG         Out to I/O location&lt;br /&gt;
&lt;br /&gt;
 POP     R31              Pop register from stack&lt;br /&gt;
 POP     R30&lt;br /&gt;
 POP     R29&lt;br /&gt;
 POP     R28&lt;br /&gt;
 POP     R27&lt;br /&gt;
 POP     R26&lt;br /&gt;
 POP     R25&lt;br /&gt;
 POP     R24&lt;br /&gt;
 POP     R23&lt;br /&gt;
 POP     R22&lt;br /&gt;
 POP     R21&lt;br /&gt;
 POP     R20&lt;br /&gt;
 POP     R19&lt;br /&gt;
 POP     R18&lt;br /&gt;
 POP     R17&lt;br /&gt;
 POP     R16&lt;br /&gt;
 POP     R11&lt;br /&gt;
 POP     R10&lt;br /&gt;
 POP     R7 &lt;br /&gt;
 POP     R5 &lt;br /&gt;
 POP     R4 &lt;br /&gt;
 POP     R3 &lt;br /&gt;
 POP     R2 &lt;br /&gt;
 POP     R1 &lt;br /&gt;
 POP     R0 &lt;br /&gt;
 RETI  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das sind 26 zusätzliche Byte auf dem Stack. Unser Beispiel 2 benötigt nur 4 Register und SREG, d.h. 5 Byte auf dem Stack.&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13960</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13960"/>
				<updated>2008-09-03T23:04:56Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Die Hardware für die Code-Beispiele: Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet noch kein Assembler und dient als Vorlage für Beispiel 2.&lt;br /&gt;
&lt;br /&gt;
Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet. Der Code ist bewusst simpel gestrickt. Trotzdem wurden bewusst mächtige Bascom Befehle wie &amp;quot;Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$&amp;quot; verwendet, um die Stärke vom Bascom gegenüber ASM herauszustellen.&lt;br /&gt;
&lt;br /&gt;
Die Uhr wird über 3 Tasten (Plus, Minus, Enter) gestellt. Jeder kennt das...&lt;br /&gt;
Beim Stellen laufen die Werte in beide Richtungen über (Beispiel Stunde : ...23,0,1....23,0,1..)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits etwas auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* der Befehl DEBONCE muss ständig gepollt werden, d.h. '''der AVR ist zu 100% beschäftigt (Batteriebetrieb!''')&lt;br /&gt;
* wird eine Taste gedrückt, '''friert der AVR für 30ms ein''' (andere Unterprogramme können in der Zeit nicht angesprungen werden)&lt;br /&gt;
* der Bascom Befehl Debounce kann '''kein Autorepeat''', d.h. langes Drücken einer Taste bewirkt keine zusätzlichen Impulse&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR liegt '''zu 99,9% der Zeit Powersave-Modus''' (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein '''Autorepeat''', d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
* kein zusätzlicher Timer erforderlich - das Unterprogramm Debounce wird an einem vorhandenen Timer rangehangen&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Die Tasten werden im Beispiel mit 128Hz abgetastet. Eine Taste muss 3x in Folge den gleichen Pegelk haben, um als Zustandswechsel anerkannt zu werden. (Verzögerung ca. 30ms).&lt;br /&gt;
 &lt;br /&gt;
Wer es etwas ruhiger mag, kann wegen der vorgegebenen Prescaler von Timer2 (1,8,32,64 etc) erst 16 Hz als nächsten Takt zum Pollen wählen. Das ergibt unangenehme Verzögerungen beim Tastendruck von ca. 0,2s.&lt;br /&gt;
&lt;br /&gt;
Andere Frequenzen erreicht man aber durch den CTC Mode. Der wird von Bascom nicht unterstützt, weshalb man die Register von Hand setzen muss. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
'...but if you love 32hz debounce use CTC (max. Verzögerung 4/32=0,125s)&lt;br /&gt;
  ' Tccr2 = &amp;amp;B0000_1010                                         'CTC und Prescale=8 @32768Hz&lt;br /&gt;
  ' Assr = &amp;amp;B0000_1000                                          'clocked from crystal Oscillator&lt;br /&gt;
  ' Ocr2 = 127                                                  '32768/(8*(127+1))=32Hz&lt;br /&gt;
  ' On Oc2 _isr_t2ovf Nosave                                   'Interrupt NOSAVE_ISR!&lt;br /&gt;
  ' Enable Oc2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in der ISR ist dann die folgende Zeile der Frequenz entsprechend anzupassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Warum eine ISR in Assembler programmieren?==&lt;br /&gt;
Ein gutes AVR-Programm hängt nicht irgendwo im Code fest. &lt;br /&gt;
Insbesonders sollten ISR sehr schnell durchlaufen werden, um andere Prozesse nicht zum Stocken zu bringen. (Infrarotsensor auslesen etc). &lt;br /&gt;
&lt;br /&gt;
Stehen nach einem Interrupt größere Sachen an (Print, LCD etc), dann setzt man in der ISR nur ein Flag und bearbeitet das Problem in der Main. Im Beispiel 2 wurde das Flag flagRTC_Changed gesetzt, um jede Sekunde das LCD zu aktualisieren. In der Variablen Key_press wird nach jedem Interrupt-Ereignis (Aufwachen aus Powersave) nachgesehen, ob eine neue Taste erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
Wird in Bascom eine ISR ohne dem Zusatz NOSAVE programmiert, sichert Bascom fast alle Register auf den Stack. Das kostet Zeit und führt zu Stacküberläufen, wenn man den Stackbereich nicht deutlich anhebt. Hier ein Bascom-Code Schnipsel einer solchen ISR.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 PUSH    R0               Push register on stack&lt;br /&gt;
 PUSH    R1 &lt;br /&gt;
 PUSH    R2 &lt;br /&gt;
 PUSH    R3 &lt;br /&gt;
 PUSH    R4 &lt;br /&gt;
 PUSH    R5 &lt;br /&gt;
 PUSH    R7 &lt;br /&gt;
 PUSH    R10&lt;br /&gt;
 PUSH    R11&lt;br /&gt;
 PUSH    R16&lt;br /&gt;
 PUSH    R17&lt;br /&gt;
 PUSH    R18&lt;br /&gt;
 PUSH    R19&lt;br /&gt;
 PUSH    R20&lt;br /&gt;
 PUSH    R21&lt;br /&gt;
 PUSH    R22&lt;br /&gt;
 PUSH    R23&lt;br /&gt;
 PUSH    R24&lt;br /&gt;
 PUSH    R25&lt;br /&gt;
 PUSH    R26&lt;br /&gt;
 PUSH    R27&lt;br /&gt;
 PUSH    R28&lt;br /&gt;
 PUSH    R29&lt;br /&gt;
 PUSH    R30&lt;br /&gt;
 PUSH    R31&lt;br /&gt;
&lt;br /&gt;
 IN      R24,SREG         In from I/O location&lt;br /&gt;
 PUSH    R24              Push register on stack&lt;br /&gt;
&lt;br /&gt;
'eigener Code in der ISR&lt;br /&gt;
&lt;br /&gt;
 POP     R24              Pop register from stack&lt;br /&gt;
 OUT     0x3F,SREG         Out to I/O location&lt;br /&gt;
&lt;br /&gt;
 POP     R31              Pop register from stack&lt;br /&gt;
 POP     R30&lt;br /&gt;
 POP     R29&lt;br /&gt;
 POP     R28&lt;br /&gt;
 POP     R27&lt;br /&gt;
 POP     R26&lt;br /&gt;
 POP     R25&lt;br /&gt;
 POP     R24&lt;br /&gt;
 POP     R23&lt;br /&gt;
 POP     R22&lt;br /&gt;
 POP     R21&lt;br /&gt;
 POP     R20&lt;br /&gt;
 POP     R19&lt;br /&gt;
 POP     R18&lt;br /&gt;
 POP     R17&lt;br /&gt;
 POP     R16&lt;br /&gt;
 POP     R11&lt;br /&gt;
 POP     R10&lt;br /&gt;
 POP     R7 &lt;br /&gt;
 POP     R5 &lt;br /&gt;
 POP     R4 &lt;br /&gt;
 POP     R3 &lt;br /&gt;
 POP     R2 &lt;br /&gt;
 POP     R1 &lt;br /&gt;
 POP     R0 &lt;br /&gt;
 RETI  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das sind 26 zusätzliche Byte auf dem Stack. Unser Beispiel 2 benötigt nur 4 Register und SREG, d.h. 5 Byte auf dem Stack.&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13959</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13959"/>
				<updated>2008-09-03T23:00:31Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Die Hardware für die Code-Beispiele: Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet noch kein Assembler und dient als Vorlage für Beispiel 2.&lt;br /&gt;
&lt;br /&gt;
Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet. Der Code ist bewusst simpel gestrickt. Trotzdem wurden bewusst mächtige Bascom Befehle wie &amp;quot;Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$&amp;quot; verwendet, um die Stärke vom Bascom gegenüber ASM herauszustellen.&lt;br /&gt;
&lt;br /&gt;
Die Uhr wird über 3 Tasten (Plus, Minus, Enter) gestellt. Jeder kennt das...&lt;br /&gt;
Beim Stellen laufen die Werte in beide Richtungen über (Beispiel Stunde : ...23,0,1....23,0,1..)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits etwas auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* der Befehl DEBONCE muss ständig gepollt werden, d.h. '''der AVR ist zu 100% beschäftigt (Batteriebetrieb!''')&lt;br /&gt;
* wird eine Taste gedrückt, '''friert der AVR für 30ms ein''' (andere Unterprogramme können in der Zeit nicht angesprungen werden)&lt;br /&gt;
* der Bascom Befehl Debounce kann '''kein Autorepeat''', d.h. langes Drücken einer Taste bewirkt keine zusätzlichen Impulse&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR liegt '''zu 99,9% der Zeit Powersave-Modus''' (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein '''Autorepeat''', d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
* kein zusätzlicher Timer erforderlich - das Unterprogramm Debounce wird an einem vorhandenen Timer rangehangen&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Die Tasten werden im Beispiel mit 128Hz abgetastet. Eine Taste muss 3x in Folge den gleichen Pegelk haben, um als Zustandswechsel anerkannt zu werden. (Verzögerung ca. 30ms).&lt;br /&gt;
 &lt;br /&gt;
Wer es etwas ruhiger mag, kann wegen der vorgegebenen Prescaler von Timer2 (1,8,32,64 etc) erst 16 Hz als nächsten Takt zum Pollen wählen. Das ergibt unangenehme Verzögerungen beim Tastendruck von ca. 0,2s.&lt;br /&gt;
&lt;br /&gt;
Andere Frequenzen erreicht man aber durch den CTC Mode. Der wird von Bascom nicht unterstützt, weshalb man die Register von Hand setzen muss. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
'...but if you love 32hz debounce use CTC (max. Verzögerung 4/32=0,125s)&lt;br /&gt;
  ' Tccr2 = &amp;amp;B0000_1010                                         'CTC und Prescale=8 @32768Hz&lt;br /&gt;
  ' Assr = &amp;amp;B0000_1000                                          'clocked from crystal Oscillator&lt;br /&gt;
  ' Ocr2 = 127                                                  '32768/(8*(127+1))=32Hz&lt;br /&gt;
  ' On Oc2 _isr_t2ovf Nosave                                   'Interrupt NOSAVE_ISR!&lt;br /&gt;
  ' Enable Oc2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in der ISR ist dann die folgende Zeile der Frequenz entsprechend anzupassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Warum eine ISR in Assembler programmieren?==&lt;br /&gt;
Ein gutes AVR-Programm hängt nicht irgendwo im Code fest. &lt;br /&gt;
Insbesonders sollten ISR sehr schnell durchlaufen werden, um andere Prozesse nicht zum Stocken zu bringen. (Infrarotsensor auslesen etc). &lt;br /&gt;
&lt;br /&gt;
Stehen nach einem Interrupt größere Sachen an (Print, LCD etc), dann setzt man in der ISR nur ein Flag und bearbeitet das Problem in der Main. Im Beispiel 2 wurde das Flag flagRTC_Changed gesetzt, um jede Sekunde das LCD zu aktualisieren. In der Variablen Key_press wird nach jedem Interrupt-Ereignis (Aufwachen aus Powersave) nachgesehen, ob eine neue Taste erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
Wird in Bascom eine ISR ohne dem Zusatz NOSAVE programmiert, sichert Bascom fast alle Register auf den Stack. Das kostet Zeit und führt zu Stacküberläufen, wenn man den Stackbereich nicht deutlich anhebt. Hier ein Bascom-Code Schnipsel einer solchen ISR.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 PUSH    R0               Push register on stack&lt;br /&gt;
 PUSH    R1 &lt;br /&gt;
 PUSH    R2 &lt;br /&gt;
 PUSH    R3 &lt;br /&gt;
 PUSH    R4 &lt;br /&gt;
 PUSH    R5 &lt;br /&gt;
 PUSH    R7 &lt;br /&gt;
 PUSH    R10&lt;br /&gt;
 PUSH    R11&lt;br /&gt;
 PUSH    R16&lt;br /&gt;
 PUSH    R17&lt;br /&gt;
 PUSH    R18&lt;br /&gt;
 PUSH    R19&lt;br /&gt;
 PUSH    R20&lt;br /&gt;
 PUSH    R21&lt;br /&gt;
 PUSH    R22&lt;br /&gt;
 PUSH    R23&lt;br /&gt;
 PUSH    R24&lt;br /&gt;
 PUSH    R25&lt;br /&gt;
 PUSH    R26&lt;br /&gt;
 PUSH    R27&lt;br /&gt;
 PUSH    R28&lt;br /&gt;
 PUSH    R29&lt;br /&gt;
 PUSH    R30&lt;br /&gt;
 PUSH    R31&lt;br /&gt;
&lt;br /&gt;
 IN      R24,SREG         In from I/O location&lt;br /&gt;
 PUSH    R24              Push register on stack&lt;br /&gt;
&lt;br /&gt;
'eigener Code in der ISR&lt;br /&gt;
&lt;br /&gt;
 POP     R24              Pop register from stack&lt;br /&gt;
 OUT     0x3F,SREG         Out to I/O location&lt;br /&gt;
&lt;br /&gt;
 POP     R31              Pop register from stack&lt;br /&gt;
 POP     R30&lt;br /&gt;
 POP     R29&lt;br /&gt;
 POP     R28&lt;br /&gt;
 POP     R27&lt;br /&gt;
 POP     R26&lt;br /&gt;
 POP     R25&lt;br /&gt;
 POP     R24&lt;br /&gt;
 POP     R23&lt;br /&gt;
 POP     R22&lt;br /&gt;
 POP     R21&lt;br /&gt;
 POP     R20&lt;br /&gt;
 POP     R19&lt;br /&gt;
 POP     R18&lt;br /&gt;
 POP     R17&lt;br /&gt;
 POP     R16&lt;br /&gt;
 POP     R11&lt;br /&gt;
 POP     R10&lt;br /&gt;
 POP     R7 &lt;br /&gt;
 POP     R5 &lt;br /&gt;
 POP     R4 &lt;br /&gt;
 POP     R3 &lt;br /&gt;
 POP     R2 &lt;br /&gt;
 POP     R1 &lt;br /&gt;
 POP     R0 &lt;br /&gt;
 RETI  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das sind 26 zusätzliche Byte auf dem Stack. Unser Beispiel 2 benötigt nur 4 Register und SREG, d.h. 5 Byte auf dem Stack.&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13958</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13958"/>
				<updated>2008-09-03T22:49:54Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Die Hardware für die Code-Beispiele: Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet noch kein Assembler und dient als Vorlage.&lt;br /&gt;
&lt;br /&gt;
Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* der Befehl DEBONCE muss ständig gepollt werden, d.h. '''der AVR ist zu 100% beschäftigt (Batteriebetrieb!''')&lt;br /&gt;
* wird eine Taste gedrückt, '''friert der AVR für 30ms ein''' (andere Unterprogramme können in der Zeit nicht angesprungen werden)&lt;br /&gt;
* der Bascom Befehl Debounce kann '''kein Autorepeat''', d.h. langes Drücken einer Taste bewirkt keine zusätzlichen Impulse&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR liegt '''zu 99,9% der Zeit Powersave-Modus''' (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein '''Autorepeat''', d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
* kein zusätzlicher Timer erforderlich - das Unterprogramm Debounce wird an einem vorhandenen Timer rangehangen&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Die Tasten werden im Beispiel mit 128Hz abgetastet. Eine Taste muss 3x in Folge den gleichen Pegelk haben, um als Zustandswechsel anerkannt zu werden. (Verzögerung ca. 30ms).&lt;br /&gt;
 &lt;br /&gt;
Wer es etwas ruhiger mag, kann wegen der vorgegebenen Prescaler von Timer2 (1,8,32,64 etc) erst 16 Hz als nächsten Takt zum Pollen wählen. Das ergibt unangenehme Verzögerungen beim Tastendruck von ca. 0,2s.&lt;br /&gt;
&lt;br /&gt;
Andere Frequenzen erreicht man aber durch den CTC Mode. Der wird von Bascom nicht unterstützt, weshalb man die Register von Hand setzen muss. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
'...but if you love 32hz debounce use CTC (max. Verzögerung 4/32=0,125s)&lt;br /&gt;
  ' Tccr2 = &amp;amp;B0000_1010                                         'CTC und Prescale=8 @32768Hz&lt;br /&gt;
  ' Assr = &amp;amp;B0000_1000                                          'clocked from crystal Oscillator&lt;br /&gt;
  ' Ocr2 = 127                                                  '32768/(8*(127+1))=32Hz&lt;br /&gt;
  ' On Oc2 _isr_t2ovf Nosave                                   'Interrupt NOSAVE_ISR!&lt;br /&gt;
  ' Enable Oc2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in der ISR ist dann die folgende Zeile der Frequenz entsprechend anzupassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Warum eine ISR in Assembler programmieren?==&lt;br /&gt;
Ein gutes AVR-Programm hängt nicht irgendwo im Code fest. &lt;br /&gt;
Insbesonders sollten ISR sehr schnell durchlaufen werden, um andere Prozesse nicht zum Stocken zu bringen. (Infrarotsensor auslesen etc). &lt;br /&gt;
&lt;br /&gt;
Stehen nach einem Interrupt größere Sachen an (Print, LCD etc), dann setzt man in der ISR nur ein Flag und bearbeitet das Problem in der Main. Im Beispiel 2 wurde das Flag flagRTC_Changed gesetzt, um jede Sekunde das LCD zu aktualisieren. In der Variablen Key_press wird nach jedem Interrupt-Ereignis (Aufwachen aus Powersave) nachgesehen, ob eine neue Taste erkannt wurde.&lt;br /&gt;
&lt;br /&gt;
Wird in Bascom eine ISR ohne dem Zusatz NOSAVE programmiert, sichert fast Bascom alle Register auf den Stack. Das kostet Zeit und führt zu Stacküberläufen, wenn man den Stackbereich nicht deutlich anhebt. Hier ein Bascom-Code Schnipsel einer solchen ISR.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 PUSH    R0               Push register on stack&lt;br /&gt;
 PUSH    R1 &lt;br /&gt;
 PUSH    R2 &lt;br /&gt;
 PUSH    R3 &lt;br /&gt;
 PUSH    R4 &lt;br /&gt;
 PUSH    R5 &lt;br /&gt;
 PUSH    R7 &lt;br /&gt;
 PUSH    R10&lt;br /&gt;
 PUSH    R11&lt;br /&gt;
 PUSH    R16&lt;br /&gt;
 PUSH    R17&lt;br /&gt;
 PUSH    R18&lt;br /&gt;
 PUSH    R19&lt;br /&gt;
 PUSH    R20&lt;br /&gt;
 PUSH    R21&lt;br /&gt;
 PUSH    R22&lt;br /&gt;
 PUSH    R23&lt;br /&gt;
 PUSH    R24&lt;br /&gt;
 PUSH    R25&lt;br /&gt;
 PUSH    R26&lt;br /&gt;
 PUSH    R27&lt;br /&gt;
 PUSH    R28&lt;br /&gt;
 PUSH    R29&lt;br /&gt;
 PUSH    R30&lt;br /&gt;
 PUSH    R31&lt;br /&gt;
&lt;br /&gt;
 IN      R24,SREG         In from I/O location&lt;br /&gt;
 PUSH    R24              Push register on stack&lt;br /&gt;
&lt;br /&gt;
'eigener Code in der ISR&lt;br /&gt;
&lt;br /&gt;
 POP     R24              Pop register from stack&lt;br /&gt;
 OUT     0x3F,SREG         Out to I/O location&lt;br /&gt;
&lt;br /&gt;
 POP     R31              Pop register from stack&lt;br /&gt;
 POP     R30&lt;br /&gt;
 POP     R29&lt;br /&gt;
 POP     R28&lt;br /&gt;
 POP     R27&lt;br /&gt;
 POP     R26&lt;br /&gt;
 POP     R25&lt;br /&gt;
 POP     R24&lt;br /&gt;
 POP     R23&lt;br /&gt;
 POP     R22&lt;br /&gt;
 POP     R21&lt;br /&gt;
 POP     R20&lt;br /&gt;
 POP     R19&lt;br /&gt;
 POP     R18&lt;br /&gt;
 POP     R17&lt;br /&gt;
 POP     R16&lt;br /&gt;
 POP     R11&lt;br /&gt;
 POP     R10&lt;br /&gt;
 POP     R7 &lt;br /&gt;
 POP     R5 &lt;br /&gt;
 POP     R4 &lt;br /&gt;
 POP     R3 &lt;br /&gt;
 POP     R2 &lt;br /&gt;
 POP     R1 &lt;br /&gt;
 POP     R0 &lt;br /&gt;
 RETI  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das sind 26 zusätzliche Byte auf dem Stack. Unser Beispiel 2 benötigt nur 4 Register und SREG, d.h. 5 Byte auf dem Stack.&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13957</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13957"/>
				<updated>2008-09-03T22:35:33Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Die Hardware für die Code-Beispiele: Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet noch kein Assembler und dient als Vorlage.&lt;br /&gt;
&lt;br /&gt;
Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* der Befehl DEBONCE muss ständig gepollt werden, d.h. '''der AVR ist zu 100% beschäftigt (Batteriebetrieb!''')&lt;br /&gt;
* wird eine Taste gedrückt, '''friert der AVR für 30ms ein''' (andere Unterprogramme können in der Zeit nicht angesprungen werden)&lt;br /&gt;
* der Bascom Befehl Debounce kann '''kein Autorepeat''', d.h. langes Drücken einer Taste bewirkt keine zusätzlichen Impulse&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR liegt '''zu 99,9% der Zeit Powersave-Modus''' (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein '''Autorepeat''', d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
* kein zusätzlicher Timer erforderlich - das Unterprogramm Debounce wird an einem vorhandenen Timer rangehangen&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Die Tasten werden im Beispiel mit 128Hz abgetastet. Eine Taste muss 3x in Folge den gleichen Pegelk haben, um als Zustandswechsel anerkannt zu werden. (Verzögerung ca. 30ms).&lt;br /&gt;
 &lt;br /&gt;
Wer es etwas ruhiger mag, kann wegen der vorgegebenen Prescaler von Timer2 (1,8,32,64 etc) erst 16 Hz als nächsten Takt zum Pollen wählen. Das ergibt unangenehme Verzögerungen beim Tastendruck von ca. 0,2s.&lt;br /&gt;
&lt;br /&gt;
Andere Frequenzen erreicht man aber durch den CTC Mode. Der wird von Bascom nicht unterstützt, weshalb man die Register von Hand setzen muss. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
'...but if you love 32hz debounce use CTC (max. Verzögerung 4/32=0,125s)&lt;br /&gt;
  ' Tccr2 = &amp;amp;B0000_1010                                         'CTC und Prescale=8 @32768Hz&lt;br /&gt;
  ' Assr = &amp;amp;B0000_1000                                          'clocked from crystal Oscillator&lt;br /&gt;
  ' Ocr2 = 127                                                  '32768/(8*(127+1))=32Hz&lt;br /&gt;
  ' On Oc2 _isr_t2ovf Nosave                                   'Interrupt NOSAVE_ISR!&lt;br /&gt;
  ' Enable Oc2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in der ISR ist dann die folgende Zeile der Frequenz entsprechend anzupassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13956</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13956"/>
				<updated>2008-09-03T22:24:39Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Die Hardware für die Code-Beispiele: Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet noch kein Assembler und dient als Vorlage.&lt;br /&gt;
&lt;br /&gt;
Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* der Befehl DEBONCE muss ständig gepollt werden, d.h. '''der AVR ist zu 100% beschäftigt (Batteriebetrieb!''')&lt;br /&gt;
* wird eine Taste gedrückt, '''friert der AVR für 30ms ein''' (andere Unterprogramme können in der Zeit nicht angesprungen werden)&lt;br /&gt;
* der Bascom Befehl Debounce kann '''kein Autorepeat''', d.h. langes Drücken einer Taste bewirkt keine zusätzlichen Impulse&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR liegt zu 99,9% der Zeit Powersave-Modus (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein Autorepeat, d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Die Tasten werden im Beispiel mit 128Hz abgetastet. Eine Taste muss 3x in Folge den gleichen Pegelk haben, um als Zustandswechsel anerkannt zu werden. (Verzögerung ca. 30ms).&lt;br /&gt;
 &lt;br /&gt;
Wer es etwas ruhiger mag, kann wegen der vorgegebenen Prescaler von Timer2 (1,8,32,64 etc) erst 16 Hz als nächsten Takt zum Pollen wählen. Das ergibt unangenehme Verzögerungen beim Tastendruck von ca. 0,2s.&lt;br /&gt;
&lt;br /&gt;
Andere Frequenzen erreicht man aber durch den CTC Mode. Der wird von Bascom nicht unterstützt, weshalb man die Register von Hand setzen muss. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
'...but if you love 32hz debounce use CTC (max. Verzögerung 4/32=0,125s)&lt;br /&gt;
  ' Tccr2 = &amp;amp;B0000_1010                                         'CTC und Prescale=8 @32768Hz&lt;br /&gt;
  ' Assr = &amp;amp;B0000_1000                                          'clocked from crystal Oscillator&lt;br /&gt;
  ' Ocr2 = 127                                                  '32768/(8*(127+1))=32Hz&lt;br /&gt;
  ' On Oc2 _isr_t2ovf Nosave                                   'Interrupt NOSAVE_ISR!&lt;br /&gt;
  ' Enable Oc2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in der ISR ist dann die folgende Zeile der Frequenz entsprechend anzupassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13955</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13955"/>
				<updated>2008-09-03T22:21:23Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Bascom ISR in Assembler programmieren */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Die Hardware für die Code-Beispiele: Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet kein Assembler. Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* Debounce muss ständig gepollt werden, d.h. der AVR ist zu 100% beschäftigt&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR für 30ms ein (andere Unterprogramme können inn der Zeit nicht angesprungen werden)&lt;br /&gt;
* Debounce besitzt kein Autorepeat, d.h. langes drücken einer Taste bewirkt keine Impulse&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR liegt zu 99,9% der Zeit Powersave-Modus (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein Autorepeat, d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Die Tasten werden im Beispiel mit 128Hz abgetastet. Eine Taste muss 3x in Folge den gleichen Pegelk haben, um als Zustandswechsel anerkannt zu werden. (Verzögerung ca. 30ms).&lt;br /&gt;
 &lt;br /&gt;
Wer es etwas ruhiger mag, kann wegen der vorgegebenen Prescaler von Timer2 (1,8,32,64 etc) erst 16 Hz als nächsten Takt zum Pollen wählen. Das ergibt unangenehme Verzögerungen beim Tastendruck von ca. 0,2s.&lt;br /&gt;
&lt;br /&gt;
Andere Frequenzen erreicht man aber durch den CTC Mode. Der wird von Bascom nicht unterstützt, weshalb man die Register von Hand setzen muss. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
'...but if you love 32hz debounce use CTC (max. Verzögerung 4/32=0,125s)&lt;br /&gt;
  ' Tccr2 = &amp;amp;B0000_1010                                         'CTC und Prescale=8 @32768Hz&lt;br /&gt;
  ' Assr = &amp;amp;B0000_1000                                          'clocked from crystal Oscillator&lt;br /&gt;
  ' Ocr2 = 127                                                  '32768/(8*(127+1))=32Hz&lt;br /&gt;
  ' On Oc2 _isr_t2ovf Nosave                                   'Interrupt NOSAVE_ISR!&lt;br /&gt;
  ' Enable Oc2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in der ISR ist dann die folgende Zeile der Frequenz entsprechend anzupassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13954</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13954"/>
				<updated>2008-09-03T22:19:54Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet kein Assembler. Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* Debounce muss ständig gepollt werden, d.h. der AVR ist zu 100% beschäftigt&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR für 30ms ein (andere Unterprogramme können inn der Zeit nicht angesprungen werden)&lt;br /&gt;
* Debounce besitzt kein Autorepeat, d.h. langes drücken einer Taste bewirkt keine Impulse&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR liegt zu 99,9% der Zeit Powersave-Modus (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein Autorepeat, d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Die Tasten werden im Beispiel mit 128Hz abgetastet. Eine Taste muss 3x in Folge den gleichen Pegelk haben, um als Zustandswechsel anerkannt zu werden. (Verzögerung ca. 30ms).&lt;br /&gt;
 &lt;br /&gt;
Wer es etwas ruhiger mag, kann wegen der vorgegebenen Prescaler von Timer2 (1,8,32,64 etc) erst 16 Hz als nächsten Takt zum Pollen wählen. Das ergibt unangenehme Verzögerungen beim Tastendruck von ca. 0,2s.&lt;br /&gt;
&lt;br /&gt;
Andere Frequenzen erreicht man aber durch den CTC Mode. Der wird von Bascom nicht unterstützt, weshalb man die Register von Hand setzen muss. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
'...but if you love 32hz debounce use CTC (max. Verzögerung 4/32=0,125s)&lt;br /&gt;
  ' Tccr2 = &amp;amp;B0000_1010                                         'CTC und Prescale=8 @32768Hz&lt;br /&gt;
  ' Assr = &amp;amp;B0000_1000                                          'clocked from crystal Oscillator&lt;br /&gt;
  ' Ocr2 = 127                                                  '32768/(8*(127+1))=32Hz&lt;br /&gt;
  ' On Oc2 _isr_t2ovf Nosave                                   'Interrupt NOSAVE_ISR!&lt;br /&gt;
  ' Enable Oc2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in der ISR ist dann die folgende Zeile der Frequenz entsprechend anzupassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13953</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13953"/>
				<updated>2008-09-03T22:05:15Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Bascom ISR in Assembler programmieren */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder Fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem einfachen Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet kein Assembler. Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* Debounce muss ständig gepollt werden, d.h. der AVR ist zu 100% beschäftigt&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR für 30ms ein (andere Unterprogramme können inn der Zeit nicht angesprungen werden)&lt;br /&gt;
* Debounce besitzt kein Autorepeat, d.h. langes drücken einer Taste bewirkt keine Impulse&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR liegt zu 99,9% der Zeit Powersave-Modus (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein Autorepeat, d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13952</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13952"/>
				<updated>2008-09-03T22:04:40Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: Entprellung einer 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet kein Assembler. Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* Debounce muss ständig gepollt werden, d.h. der AVR ist zu 100% beschäftigt&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR für 30ms ein (andere Unterprogramme können inn der Zeit nicht angesprungen werden)&lt;br /&gt;
* Debounce besitzt kein Autorepeat, d.h. langes drücken einer Taste bewirkt keine Impulse&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: Entprellung einer 3 Tasten Uhr in einer Bascom-ISR mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR liegt zu 99,9% der Zeit Powersave-Modus (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein Autorepeat, d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13951</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13951"/>
				<updated>2008-09-03T22:02:37Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom ISR in Assembler programmieren ==&lt;br /&gt;
&lt;br /&gt;
Da immer wieder fragen zur Assembler-ISR-Programmierung in Bascom auftreten, will ich hier an einem Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet kein Assembler. Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* Debounce muss ständig gepollt werden, d.h. der AVR ist zu 100% beschäftigt&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR für 30ms ein (andere Unterprogramme können inn der Zeit nicht angesprungen werden)&lt;br /&gt;
* Debounce besitzt kein Autorepeat, d.h. langes drücken einer Taste bewirkt keine Impulse&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: 3 Tasten Uhr in Bascom mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden. Die Programmstruktur (3 Tasten Uhr) wird zum direkten Vergleich beibehalten.&lt;br /&gt;
&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die vorhanden Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. &lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt die Schwächen des Bascom Befehls DEBOUNCE:&lt;br /&gt;
* der AVR liegt zu 99,9% der Zeit Powersave-Modus (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein Autorepeat, d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsfolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
Der Code ist umfangreich kommentiert.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
&lt;br /&gt;
* [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User]]&lt;br /&gt;
* [[LCD-Modul_am_AVR]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten]  - AVR-Tutorial: Tasten&lt;br /&gt;
 * [http://www.mikrocontroller.net/articles/Entprellung] - Entprellung&lt;br /&gt;
 * [http://www.mikrocontroller.net/topic/6492] - Tasten entprellen - Bulletproof|&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13950</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13950"/>
				<updated>2008-09-03T21:52:37Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Bascom ISR in Assembler programmieren */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom ISR in Assembler programmieren ==&lt;br /&gt;
Da immer wieder fragen zur Assembler Programmierung in Bascom auftreten, will ich hier an einem Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
'''Die Hardware'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce.jpg]]&lt;br /&gt;
&lt;br /&gt;
Die Schaltung ist auf einem Steckbrett schnell gesteckt. Der zusätzliche Max232 dient nur der einfachen Bootloader-Programmierung.&lt;br /&gt;
&lt;br /&gt;
zur Hilfestellung für Einsteiger noch der Schaltplan:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Clock_Debounce_Board.png]] &lt;br /&gt;
&lt;br /&gt;
Der u.g. Beispiel-Code lässt sich auf alle AVR-Starterplatinen mit Tasten+LCD schnell anpassen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet kein Assembler. Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* Debounce muss ständig gepollt werden, d.h. der AVR ist zu 100% beschäftigt&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR für 30ms ein (andere Unterprogramme können inn der Zeit nicht angesprungen werden)&lt;br /&gt;
* Debounce besitzt kein Autorepeat, d.h. langes drücken einer Taste bewirkt keine Impulse&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: 3 Tasten Uhr in Bascom mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden.&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt obige Schwächen:&lt;br /&gt;
* der AVR liegt zu 99,9% der Zeit Powersave-Modus (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein Autorepeat, d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsefolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Clock_Debounce_Board.png&amp;diff=13949</id>
		<title>Datei:Clock Debounce Board.png</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Clock_Debounce_Board.png&amp;diff=13949"/>
				<updated>2008-09-03T21:49:22Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: einfache 3 Tasten Uhr mit Quarz&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;einfache 3 Tasten Uhr mit Quarz&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Clock_Debounce.jpg&amp;diff=13948</id>
		<title>Datei:Clock Debounce.jpg</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Clock_Debounce.jpg&amp;diff=13948"/>
				<updated>2008-09-03T21:32:43Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: Atmega8 mit LCD und 3 Tasten&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Atmega8 mit LCD und 3 Tasten&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13947</id>
		<title>Bascom Debounce ISR in Assembler</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_Debounce_ISR_in_Assembler&amp;diff=13947"/>
				<updated>2008-09-03T21:28:35Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom ISR in Assembler programmieren ==&lt;br /&gt;
Da immer wieder fragen zur Assembler Programmierunf in Bascom auftreten, will ich hier an einem Beispiel einer 3 Tasten Uhr mit LCD Display den Weg aufzeigen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel 1: 3 Tasten Uhr in Bascom ==&lt;br /&gt;
Der nachfolgende Code verwendet kein Assembler. Es wurde zur Entprellung der Tasten der Bascom Befehl DEBONCE verwendet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
Config Clock = Soft&lt;br /&gt;
Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 1                                       'first index of date_array is 1&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
  Debounce Pinb.3 , 0 , Key_plus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.4 , 0 , Key_minus_pressed , Sub&lt;br /&gt;
  Debounce Pinb.5 , 0 , Key_enter_pressed , Sub&lt;br /&gt;
&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
'********* Gusub  ***************************************************&lt;br /&gt;
Key_plus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_minus_pressed:&lt;br /&gt;
   Gosub Date_values&lt;br /&gt;
   If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
   Date_array(setdatetime_index) = J&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Key_enter_pressed:&lt;br /&gt;
   If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Date_values&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Adjusts the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Date_values:&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(Setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der Code wurde bereits auf Länge optimiert (im Rahmen einer geboteten Übersichtlichkeit des Quelltextes).&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1376 Byte]&lt;br /&gt;
&lt;br /&gt;
Trotzdem besitzt das Beispiel einige Schwächen:&lt;br /&gt;
* Debounce muss ständig gepollt werden, d.h. der AVR ist zu 100% beschäftigt&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR für 30ms ein (andere Unterprogramme können inn der Zeit nicht angesprungen werden)&lt;br /&gt;
* Debounce besitzt kein Autorepeat, d.h. langes drücken einer Taste bewirkt keine Impulse&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beispiel 2: 3 Tasten Uhr in Bascom mit Assembler-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
Die Schwächen des Beispiel 1 sollen ausgebügelt werden.&lt;br /&gt;
Dazu wird der Bascom Befehl Debounce in Assembler nachgebaut. Um den AVR zu entlasten wird die Debounce Routine in die Timer2-ISR gehangen. Da im Beispiel 1 Timer2 asynchron mit externem 32kHz-Quarz gefahren wird, ist dieser Timer2 bereits von der Bascom Softclock (Takt 1Hz) belegt. Im Beispiel wird gezeigt, wie man sich an die Softclock heranhängen kann. Gleichzeitig wird der Takt der Timer2-ISR auf 128Hz erhöht, um die Tasten zu pollen.&lt;br /&gt;
Zum Entprellen wurde der bekannte Code von Peter Dannegger (Tasten entprellen - Bulletproof) angepasst. &lt;br /&gt;
Der techn. Hintergrund wird in dem ausführlichen Artikel http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten erläutert.&lt;br /&gt;
&lt;br /&gt;
Der Code beseitigt obige Schwächen:&lt;br /&gt;
* der AVR liegt zu 99,9% der Zeit Powersave-Modus (nur wenige µA Stromverbrauch)&lt;br /&gt;
* wird eine Taste gedrückt, friert der AVR nicht ein&lt;br /&gt;
* Debounce besitzt jetzt ein Autorepeat, d.h. langes drücken einer Taste bewirkt nach ca. 1sec eine Impulsefolge von 8Hz (beliebig einstellbar)&lt;br /&gt;
Die Entprellzeit beträgt übrigens im Beispiel wie bei Bascom ca. 30ms.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
&lt;br /&gt;
Config Portb.1 = Output                                     'Ein Pin wird als LED-Ausgang konfiguriert&lt;br /&gt;
&lt;br /&gt;
'********* LCD ***************************************************&lt;br /&gt;
 'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0&lt;br /&gt;
 Config Lcdmode = Port&lt;br /&gt;
 Config Lcdbus = 4                                          '4 bit mode&lt;br /&gt;
 Config Lcd = 20 * 4&lt;br /&gt;
 Initlcd&lt;br /&gt;
 Cursor Off&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
 Config Clock = User&lt;br /&gt;
&lt;br /&gt;
'you can debounce with 128Hz (max. Verzögerung 4/128=0,031s)&lt;br /&gt;
 Config Timer2 = Timer , Async = On , Prescale = 1          'Prescaler 8-&amp;gt;16Hz or 1-&amp;gt;128Hz&lt;br /&gt;
 On Ovf2 _isr_t2ovf Nosave&lt;br /&gt;
 Enable Ovf2&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts&lt;br /&gt;
&lt;br /&gt;
 Config Date = Dmy , Separator = /&lt;br /&gt;
 _sec = 00 : _min = 15 : _hour = 14 : _day = 30 : _month = 05 : _year = 08&lt;br /&gt;
&lt;br /&gt;
Dim Flagrtc_changed As Bit&lt;br /&gt;
Dim Tick_128hz As Byte&lt;br /&gt;
&lt;br /&gt;
'********* input key and debounce ***************************************************&lt;br /&gt;
'Zuordnung der Tasten zu den Pins - diese müssen alle am selben Port hängen!&lt;br /&gt;
Const Key_plus = &amp;amp;B0000_1000                                '1=BUTTON_INPUT&lt;br /&gt;
Const Key_minus = &amp;amp;B0001_0000&lt;br /&gt;
Const Key_enter = &amp;amp;B0010_0000&lt;br /&gt;
&lt;br /&gt;
Const Pinb_mask = Key_plus + Key_minus + Key_enter          'Maske zum Filtern der Eingänge&lt;br /&gt;
Const Pinb_mask_compl = &amp;amp;HFF - Pinb_mask                    'Pinb_mask compliment&lt;br /&gt;
&lt;br /&gt;
'hier sind die Tasten am PortB&lt;br /&gt;
Ddrb = Ddrb And Pinb_mask_compl                             'DDRB: 0=INPUT 1=OUTPUT&lt;br /&gt;
Portb = Portb Or Pinb_mask                                  '1=activate internal pull-up resistors for inputs&lt;br /&gt;
Key_port Alias Pinb&lt;br /&gt;
&lt;br /&gt;
Dim Key_state As Byte                                       '0/1 invertierter Status der Tasten (1=gedrückt)&lt;br /&gt;
Dim Key_press As Byte                                       'Trigger für gedrückte Taste&lt;br /&gt;
Dim Key_rep As Byte                                         'Wiederholungszähler für Autorepeat&lt;br /&gt;
Dim Key_ct0 As Byte , Key_ct1 As Byte                       '2 Bit Zähler für Entprellung&lt;br /&gt;
&lt;br /&gt;
Const Key_repeat_start = 127                                'Anzahl-1 der Timer-ISR bis Autorepeat beginnt (ca. 500-1000ms)&lt;br /&gt;
Const Key_repeat_next = 15                                  'Anzahl-1 der Timer-ISR für Autorepeat Wiederholrate (ca. 125ms)&lt;br /&gt;
&lt;br /&gt;
'debounce initial values&lt;br /&gt;
Key_ct0 = 255                                               'Programmstart bei gedrückten Tasten abfangen&lt;br /&gt;
Key_ct1 = 255&lt;br /&gt;
'Key_rep = Key_repeat_start                                 'Paranoia&lt;br /&gt;
&lt;br /&gt;
'********* allgemeines  ***************************************************&lt;br /&gt;
Dim I As Byte , J As Byte , K As Byte , L As Byte&lt;br /&gt;
&lt;br /&gt;
Dim Setdatetime_index As Byte&lt;br /&gt;
Setdatetime_index = 2                                       'first index of date_array is 1, 2=_min&lt;br /&gt;
&lt;br /&gt;
Dim Date_array(6) As Byte At _sec Overlay                   'array for _sec/_min/_hour/_day/_month/_year&lt;br /&gt;
&lt;br /&gt;
'********* main loop  ***************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
  Home                                                      'cursor home&lt;br /&gt;
  Lcd Date$ ; &amp;quot;  &amp;quot; ; Time$                                  'show the date and time&lt;br /&gt;
  Locate 2 , 1&lt;br /&gt;
  Lcd &amp;quot;Set: &amp;quot; ; Lookupstr(setdatetime_index , Data_dateset)&lt;br /&gt;
&lt;br /&gt;
'zum Stromsparen wird die 128Hz-Schleife in ASM geschieben:&lt;br /&gt;
   Flagrtc_changed = 0&lt;br /&gt;
   Enable Interrupts                                        'Paranoia&lt;br /&gt;
   'Do Powersave Loop Until ((Flagrtc_changed = 1) OR (Key_press&amp;gt;0))&lt;br /&gt;
Main_1:&lt;br /&gt;
   Powersave                                                'Timer 2 läuft weiter&lt;br /&gt;
   lds     r16,{Key_press}                                  'Taste gedrückt?&lt;br /&gt;
   TST     r16&lt;br /&gt;
   BRNE    main_2&lt;br /&gt;
   lds     r16,{flagRTC_Changed}                            'eine neue Sekunde?&lt;br /&gt;
   sbrS    r16,bit.flagRTC_Changed&lt;br /&gt;
   RJMP    Main_1&lt;br /&gt;
Main_2:&lt;br /&gt;
&lt;br /&gt;
   If Key_press &amp;gt; 0 Then Gosub Setdatetime_input&lt;br /&gt;
Loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Setdatetime_input&lt;br /&gt;
'Call from:  main&lt;br /&gt;
'Purpose:    Set the Date and Clock&lt;br /&gt;
'Parameters: key input&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdatetime_input:&lt;br /&gt;
&lt;br /&gt;
 Disable Interrupts                                         'ISR sperren&lt;br /&gt;
    I = Key_press                                           'key auslesen&lt;br /&gt;
    Key_press = 0&lt;br /&gt;
&lt;br /&gt;
    L = Lookup(setdatetime_index , Date_max)&lt;br /&gt;
    If Setdatetime_index = 4 Then&lt;br /&gt;
       L = Lookup(_month , Date_month)&lt;br /&gt;
       K = _year And 3&lt;br /&gt;
       If _month = 2 Then If K = 0 Then Incr L&lt;br /&gt;
    End If&lt;br /&gt;
    K = Lookup(setdatetime_index , Date_min)&lt;br /&gt;
&lt;br /&gt;
    J = Date_array(setdatetime_index)&lt;br /&gt;
    If I = Key_plus Then&lt;br /&gt;
        If J &amp;lt; L Then Incr J Else J = K&lt;br /&gt;
    End If&lt;br /&gt;
    If I = Key_minus Then&lt;br /&gt;
        If J &amp;gt; K Then Decr J Else J = L&lt;br /&gt;
    End If&lt;br /&gt;
    Date_array(setdatetime_index) = J&lt;br /&gt;
&lt;br /&gt;
 Enable Interrupts                                          'ISR freigeben&lt;br /&gt;
&lt;br /&gt;
    If I = Key_enter Then&lt;br /&gt;
        If Setdatetime_index &amp;lt; 6 Then Incr Setdatetime_index Else Setdatetime_index = 1&lt;br /&gt;
    End If&lt;br /&gt;
&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* ISR Timer 2 ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: _isr_t2ovf&lt;br /&gt;
'Call from:  Timer2 (128Hz)&lt;br /&gt;
'Purpose:    RTC, Debounce&lt;br /&gt;
'Result:     RTC:  _sec, _min, _hour, _day, _month, _year&lt;br /&gt;
'            Debounce: siehe unten&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
 $external _soft_clock&lt;br /&gt;
 Const _sectic = 0                                          'Compilerstatement for CONFIG CLOCK = USER , GOSUB &amp;lt;&amp;gt; SECTIC&lt;br /&gt;
&lt;br /&gt;
_isr_t2ovf:&lt;br /&gt;
$asm&lt;br /&gt;
        push    r16&lt;br /&gt;
        in      r16,sreg&lt;br /&gt;
        push    r16                                         'save R16 and SREG in stack&lt;br /&gt;
&lt;br /&gt;
'  debounce key&lt;br /&gt;
        push    r17                                         'now save all used registers&lt;br /&gt;
        push    r18&lt;br /&gt;
        PUSH    r19&lt;br /&gt;
       !Call Debounce_port&lt;br /&gt;
        POP     r19                                         'restore registers&lt;br /&gt;
        pop     r18&lt;br /&gt;
        pop     r17&lt;br /&gt;
&lt;br /&gt;
        lds     r16,{tick_128hz}                            'increment _tick128 every 1/128s&lt;br /&gt;
        inc     r16&lt;br /&gt;
        sts     {tick_128hz},r16&lt;br /&gt;
        andi    r16,128-1                                   'if _tick128 is not an 128 multiple&lt;br /&gt;
        brne    _T2OVF_END                                  '   set_digit exit isr, else a second has passed&lt;br /&gt;
&lt;br /&gt;
'  this bit variable will be set up every new second&lt;br /&gt;
        lds     r16,{flagRTC_Changed}                       'bit variable&lt;br /&gt;
        sbr     r16,2^bit.flagRTC_Changed&lt;br /&gt;
        sts     {flagRTC_Changed},r16                       'it's the main code duty to reset the flag after noticing it&lt;br /&gt;
&lt;br /&gt;
'  go to internal Bascom ISR-Routine: Softclock&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
        JMP    _SOFT_CLOCK                                  'original RETI&lt;br /&gt;
&lt;br /&gt;
_t2ovf_end:&lt;br /&gt;
        pop     r16                                         'get content of SREG&lt;br /&gt;
        !out    sreg,r16                                    'restore sreg&lt;br /&gt;
        pop     r16&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
Return                                                      'and exit isr with RETI&lt;br /&gt;
&lt;br /&gt;
'********* Debounce  ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Debounce_port&lt;br /&gt;
'            gleichzeitige Erkennnung von max. 8 gedrückten Tasten mit Entprellung und Autorepeat&lt;br /&gt;
'            jede Taste wird für einen Statuswechsel 3x abgetastet&lt;br /&gt;
'verwendete Register:  R16, R17, R18, R19&lt;br /&gt;
'Variablen:  Key_state    Status der Tasten 0/1&lt;br /&gt;
'            Key_rep      Wiederholungszähler für Autorepeat&lt;br /&gt;
'            Key_ct0 / Key_ct1  2-Bit-Zähler für Entprellung&lt;br /&gt;
'Result:     Key_press:   wechselt zu 1 bei Erkennung einer gedrückten Taste&lt;br /&gt;
'                         Status von Key_press in Main nach Auslesen zurücksetzen!&lt;br /&gt;
'Thanks to Peter Dannegger&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Debounce_port:                                              'ca. 108 Byte&lt;br /&gt;
  $asm&lt;br /&gt;
    in R18, key_port&lt;br /&gt;
    com R18                                                 'pin low is active&lt;br /&gt;
    ANDI R18, Pinb_mask                                     'filter pins with Pinb_mask&lt;br /&gt;
    LDS R19, {Key_state}&lt;br /&gt;
    eor R18, R19                                            'detect level change at pin input&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {Key_ct0}                                      'the 8 channels 2-Bit-Counter&lt;br /&gt;
    LDS R17, {Key_ct1}&lt;br /&gt;
    and R16, R18                                            'reset the 2-bit-counter in R17:R16&lt;br /&gt;
    and R17, R18&lt;br /&gt;
    com R16                                                 'decrement R17:R16&lt;br /&gt;
    eor R17, R16&lt;br /&gt;
    STS {Key_ct0} , R16                                     'save counter&lt;br /&gt;
    STS {Key_ct1} , R17&lt;br /&gt;
&lt;br /&gt;
    and R18, R16                                            'IF counter = &amp;amp;B11 set_digit input debounced&lt;br /&gt;
    and R18, R17&lt;br /&gt;
    eor R19, R18                                            '0&amp;lt;-&amp;gt;1 toggle  state&lt;br /&gt;
    STS {Key_state} , R19&lt;br /&gt;
&lt;br /&gt;
    LDS R16, {key_press}&lt;br /&gt;
    and R18, R19&lt;br /&gt;
    or R16, R18                                             '0-&amp;gt;1 key press detected&lt;br /&gt;
&lt;br /&gt;
    LDS R17, {Key_rep}&lt;br /&gt;
    tst R19                                                 'Key_state: irgendeine Taste gedrückt?&lt;br /&gt;
    breq Key_isr_rep&lt;br /&gt;
    dec R17&lt;br /&gt;
    brpl Key_isr_finish                                     'IF Key_rep&amp;lt;0 set_digit Wartezeit abgelaufen&lt;br /&gt;
    mov R16, R19                                            'autorepeat key_press = key_state&lt;br /&gt;
    ldi R17, key_repeat_next                                'set autorepeat repeat timer&lt;br /&gt;
    rjmp Key_isr_finish&lt;br /&gt;
&lt;br /&gt;
Key_isr_rep:&lt;br /&gt;
    ldi R17, key_repeat_start                               'reset autorepeat start timer&lt;br /&gt;
&lt;br /&gt;
Key_isr_finish:&lt;br /&gt;
    STS {Key_rep} , R17                                     'save the autorepeat timer&lt;br /&gt;
    STS {key_press} , R16                                   'save debounced key&lt;br /&gt;
  $end Asm&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* RTC ***************************************************&lt;br /&gt;
'the following void routines are needed if you want to use&lt;br /&gt;
'    the time/date bascom functions. You may fill them as you need&lt;br /&gt;
Getdatetime:&lt;br /&gt;
    Return&lt;br /&gt;
Settime:&lt;br /&gt;
    Return&lt;br /&gt;
Setdate:&lt;br /&gt;
    Return&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'******** Select Date and Time ************************************&lt;br /&gt;
Date_min:&lt;br /&gt;
Data 0 , 0 , 0 , 0 , 1 , 1 , 0&lt;br /&gt;
Date_max:&lt;br /&gt;
Data 0 , 59 , 59 , 23 , 31 , 12 , 99&lt;br /&gt;
Date_month:&lt;br /&gt;
Data 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31&lt;br /&gt;
Data_dateset:&lt;br /&gt;
Data &amp;quot; &amp;quot; , &amp;quot;Sec &amp;quot; , &amp;quot;Min &amp;quot; , &amp;quot;Hour&amp;quot; , &amp;quot;Day &amp;quot; , &amp;quot;Mon &amp;quot; , &amp;quot;Year&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Getestet mit Bascom Version BASCOM 1.11.9.1 [Codelänge 1482 Byte]&lt;br /&gt;
Der geringe Codezuwachs von 106 Byte ist der erweiterten Funktionalität geschuldet. Der Code ließe sich durch weitere Anwendung von Assembler problemlos einkürzen, jedoch soll hier ein übersichtlicher der Weg zum Einstieg aufgezeigt werden.&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User&amp;diff=13946</id>
		<title>Assembler Einführung für Bascom-User</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User&amp;diff=13946"/>
				<updated>2008-09-03T20:58:11Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Was hier folgt, ist nichts für Profis und Power-User, die mögen weiterblättern. Ich versuche hier, absolute Neueinsteiger nach und nach mit ein paar Grundinformationen zu versorgen. &lt;br /&gt;
&lt;br /&gt;
==Assembler Einführung für Bascom-User==&lt;br /&gt;
&lt;br /&gt;
===Wieso Bascom ?===&lt;br /&gt;
Eine der einfachsten Möglichkeiten, sich an Assembler heranzutasten, ist es, den Bascom-Compiler als Workbench zu benutzen. &lt;br /&gt;
&lt;br /&gt;
Die Vorteile:&lt;br /&gt;
*Das Drumherum mit der richtigen Initialisierung, auch der Peripherie, kann man bequem von Bascom machen lassen, bis man sich halt auskennt.  &lt;br /&gt;
*Wenn irgendeine Berechnung oder Teil-Funktion nervt oder nicht gleich richtig hinhaut, schreibt man halt doch ein paar Bascom-Statements. &lt;br /&gt;
*fürs Erste reicht die Demo-Version allemal&lt;br /&gt;
&lt;br /&gt;
Die Nachteile:&lt;br /&gt;
*Gott-weiß-wie komfortabel ist der Bascom-Assembler natürlich nicht, aber es reicht. &lt;br /&gt;
*Bei manchen Befehlen ist es nicht klar, ob das ein Assembler oder ein Bascom-Befehl ist. In diesem Fall muß man ein &amp;quot;!&amp;quot; Rufzeichen davor setzen. Man erkennt das aber sofort, denn diese reservierten Bascom-Wort mach er sofort in '''Fettschrift'''. Trotzdem aufpassen !&lt;br /&gt;
&lt;br /&gt;
==Ein Grund-Programm==&lt;br /&gt;
Nicht lachen, auch das ist ein Bascom-Programm:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$asm&lt;br /&gt;
   &lt;br /&gt;
$end Asm&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das Programm macht natürlich überhaupt nix. Aber durch die paar Zeilen hat Bascom alle &lt;br /&gt;
[[AVR_Assembler_Einf%C3%BChrung#Struktur_eines_AVR-Maschinenprogrammes|notwendigen Initialisierungen]] schon erledigt und wir brauchen uns um nichts zu kümmern.&lt;br /&gt;
Zwischen &amp;quot;$asm&amp;quot; und &amp;quot;$end asm&amp;quot; kann man nun nach Herzenslust irgendwas Assemblermäßiges reinschreiben und mit dem Simulator rumprobieren. &lt;br /&gt;
&lt;br /&gt;
Auch &amp;quot;REGFILE&amp;quot; müßte man nicht hinschreiben, dann gilt eben das, was man in &amp;quot;OPTIONS/COMPILER/CHIP&amp;quot; eingestellt hat.&lt;br /&gt;
&lt;br /&gt;
==Der Zentral-Prozessor (CPU)==&lt;br /&gt;
Das ist der Kollege, dem man mit &amp;quot;Assembler-Instruktionen&amp;quot; davon überzeugen muß, irgendwas zu tun. Ohne den läuft garnix. Der hat als Hilfe einen &amp;quot;'''Befehlszähler'''&amp;quot; (PC), der immer auf den nächsten Befehl zeigt, der drankommt. Und dann hat er noch eine Reihe &amp;quot;'''Register'''&amp;quot;, das sind kleine Zwischenspeicher, mit denen er arbeiten kann. Die heissen einfach &amp;quot;R0&amp;quot;, &amp;quot;R1&amp;quot;,....&amp;quot;R31&amp;quot;, also 32 Stück, in jedes paßt genau ein Byte, und ein Byte, das wissen wir, besteht wiederum aus 8 Bits.&lt;br /&gt;
&lt;br /&gt;
[[Atmel_Controller_Mega16_und_Mega32#CPU|Hübsche Zeichnungen von Atmega32-CPU und einige weiterführende Information]] &lt;br /&gt;
&lt;br /&gt;
===Registerverwendung===&lt;br /&gt;
An sich sind wir zwischen &amp;quot;$asm&amp;quot; und &amp;quot;$end asm&amp;quot; alleiniger Herrscher über den Mikrokontroller. Damit aber auch dem Bascom ein bißchen was zu Leben bleibt, sollten wir einige Register entweder in Ruhe lassen oder erst sichern und dann wieder herstellen. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 R4, R5         ' die beiden verwendet Bascom für temporäre Sachen. &lt;br /&gt;
 R6             ' da speichert er einige Schalter (Bits)&lt;br /&gt;
 R8, R9         ' verwendet es für  &amp;quot;READ&amp;quot; und &amp;quot;RESTORE&amp;quot; etc. &lt;br /&gt;
                ' haben wir sowas aber garnicht, ist es egal&lt;br /&gt;
 R28, R29       ' braucht Bascom aber nur, wenn wir Funktionen und Subs aufrufen. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen brauchen wir uns aber darum nicht zu kümmern, es wird nix davon gebraucht. Ich wollt' es nur gesagt haben.&lt;br /&gt;
&lt;br /&gt;
===Daten-Transfer Operationen===&lt;br /&gt;
Bevor wir mit diesen Registern irgendetwas ausprobieren können, müssen wir erstmal gezielt bestimmte Werte reinschreiben können. Sowas heißt eben &amp;quot;Transfer&amp;quot;. Da wir ja erst am Anfang sind, reicht uns zum Beispiel:&lt;br /&gt;
 LDI   R24, 14&lt;br /&gt;
Damit wird in das Register R24 der Binärwert von &amp;quot;14&amp;quot; reingestellt, das sind die Bits &amp;quot;00001110&amp;quot;. Der maximale Wert, da es ja nur ein Byte ist, wäre &amp;quot;255&amp;quot;, also &amp;quot;11111111&amp;quot;. &lt;br /&gt;
Für den Befehl &amp;quot;LDI&amp;quot; können wir übrigens leider nur die Register R16 - R31 setzen, das ist so eine Einschränkung von wegen &amp;quot;RISC&amp;quot; Architektur.&lt;br /&gt;
 MOV   R3, R24&lt;br /&gt;
Deswegen auch der zweite Befehl &amp;quot;MOV&amp;quot;, damit wird im Beispiel der Inhalt von R24 in das Register R3 kopiert. Somit können wir mit maximal zwei Befehlen also jeder beliebige Register von R0 bis R31 mit beliebigen Werten laden. &lt;br /&gt;
Natürlich gibt es noch eine Menge mehr an Transferbefehlen, aber Listen von Assembler-Befehlen gibt es schon genug, da brauchen wir hier nicht auch noch eine.&lt;br /&gt;
&lt;br /&gt;
===Arithmetisch-Logische Operationen===&lt;br /&gt;
Laden wir mal zwei Register:&lt;br /&gt;
 LDI   R25, 17&lt;br /&gt;
 LDI   R24, 14&lt;br /&gt;
&lt;br /&gt;
Und jetzt die Grund-Befehle, Varianten später:&lt;br /&gt;
*Arithmetisch&lt;br /&gt;
 ADD   R25, R24       addieren      R25 + R24, Ergebnis nach R25&lt;br /&gt;
 !SUB   R25, R24       subtrahieren&lt;br /&gt;
*Logisch &lt;br /&gt;
 !AND   R25, R24       &amp;quot;UND&amp;quot;&lt;br /&gt;
 !OR    R25, R24       &amp;quot;ODER&amp;quot;&lt;br /&gt;
 EOR   R25, R24       &amp;quot;Exklusiv-ODER&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis steht immer in Operand-1&lt;br /&gt;
&lt;br /&gt;
===Gleich mal ausprobieren===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$asm&lt;br /&gt;
 LDI   R25, 17        ' Laden&lt;br /&gt;
 LDI   R24, 14        ' Laden&lt;br /&gt;
 ADD   R25, R24       'addieren  17 + 14, Ergebnis in R25&lt;br /&gt;
&lt;br /&gt;
 LDI   R25, 17        'Nachladen, da R25 durch &amp;quot;ADD&amp;quot; ja verändert wurde&lt;br /&gt;
 !SUB   R25, R24       'subtrahieren  17 - 14&lt;br /&gt;
&lt;br /&gt;
 LDI   R25, 17        ' Laden&lt;br /&gt;
 LDI   R24, 14        ' Laden&lt;br /&gt;
 !AND   R25, R24       ' Es kommt überall dort &amp;quot;1&amp;quot; raus, wo sowohl r25 als auch R24 eine 1 haben&lt;br /&gt;
 &lt;br /&gt;
 LDI   R25, 17        ' Laden&lt;br /&gt;
 LDI   R24, 14        ' Laden&lt;br /&gt;
 !OR    R25, R24       ' Es kommt überall dort &amp;quot;1&amp;quot; raus, wo r25 oder R24 eine 1 haben&lt;br /&gt;
                      '  (ODER BEIDE !)&lt;br /&gt;
&lt;br /&gt;
 LDI   R25, 17        ' Laden&lt;br /&gt;
 LDI   R24, 14        ' Laden&lt;br /&gt;
 EOR   R25, R24       ' Es kommt überall dort &amp;quot;1&amp;quot; raus, wo ENTWEDER  r25 oder R24 eine 1 haben&lt;br /&gt;
                      '  (ABER NICHT BEIDE !)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Probieren ist das am besten mit dem Simulator. (Register-Fenster öffnen und Einzelschritte)&lt;br /&gt;
&lt;br /&gt;
===Ergebnis prüfen===&lt;br /&gt;
Normalerweise ist es ja nicht so, daß vor solchen Operationen die Rechenwerte direkt geladen werden, sondern die kommen ja von irgendwo aussen her. Und da muß man ja dann anders reagieren, je nachdem, ob die Werte gleich waren, ob r25 größer oder kleiner als r24 war, und so weiter. &lt;br /&gt;
&lt;br /&gt;
Da helfen die &amp;quot;Flags&amp;quot; im '''Status-Register''' (SREG). Das ist zwar auch ein normales Byte, nur haben die einzelnen Bits darin eine spezielle Bedeutung und geben eben nähere Auskunft über die gerade abgelaufenen Operation. Nur das Wichtigste:&lt;br /&gt;
*ZERO-Bit  Es wird automatisch gesetzt, wenn das Ergebnis genau NULL ergeben hat.  &lt;br /&gt;
*CARRY-Bit Es wird automatisch gesetzt, wenn es einen &amp;quot;Übertrag&amp;quot; gegeben hat&lt;br /&gt;
&lt;br /&gt;
Man kann diese (und noch andere) Flags sehen, wenn man im Simulator auf &amp;quot;µP&amp;quot; drückt. &lt;br /&gt;
 Z = ZERO&lt;br /&gt;
 C = CARRY&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LDI   R25, 17       &lt;br /&gt;
LDI   R24, 14       &lt;br /&gt;
!SUB   R25, R24       &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Zero &amp;amp; Carry sind nicht gesetzt, denn das Ergebnis ist ungleich NULL, und &amp;quot;17&amp;quot; ist außerdem größer als &amp;quot;14&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LDI   R25, 17       &lt;br /&gt;
LDI   R24, 17       &lt;br /&gt;
!SUB   R25, R24       &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt ist Zero gesetzt, denn das Ergebnis ist gleich NULL&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LDI   R25, 12       &lt;br /&gt;
LDI   R24, 44       &lt;br /&gt;
!SUB   R25, R24       &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt ist das Carry-Bit gesetzt, denn &amp;quot;12&amp;quot; ist ja kleiner als &amp;quot;44&amp;quot;, das Ergebnis ist also negativ, und ein &amp;quot;Übertrag&amp;quot; ist auch aufgetreten.&lt;br /&gt;
&lt;br /&gt;
===Vergleichen===&lt;br /&gt;
&amp;quot;Vergleichen&amp;quot; ist für die ALU (Recheneinheit) das Gleiche wie Subtrahieren (SUB), nur daß das eigentliche Rechenergebnis nirgends hingeschrieben wird und NUR DIE FLAGS gesetzt werden. &lt;br /&gt;
 CP  R25, R24       &lt;br /&gt;
&lt;br /&gt;
===Verzweigen===&lt;br /&gt;
Wir haben ja gesagt, es wird verglichen, damit der Rechner je nach Vergleichs- der Rechenergebnis was anderes tut. &amp;quot;Was anderes tun&amp;quot; heißt anderer Code, also muß der &amp;quot;Befehlszähler&amp;quot; einen anderen Wert bekommen, damit der Programmablauf dort fortgesetzt wird. Dazu gibt es natürlich die &amp;quot;unbedingten&amp;quot; Varianten&lt;br /&gt;
 JMP  Zieladresse  ' oder&lt;br /&gt;
 RJMP Zieladresse  ' das nimmt man, wenn das Ziel in der Nähe ist&lt;br /&gt;
Oder eben die &amp;quot;Verzweigung unter bestimmten Bedingungen&amp;quot; (conditional branch) &lt;br /&gt;
 BRxxx Zieladresse &lt;br /&gt;
Für &amp;quot;xxx&amp;quot; (Bedingung) gibt es nun eine ganze Reihe Möglichkeiten. Es gibt im Prinzip für jedes Bit im Status-Register (s.o) eine Abfrage &amp;quot;wenn gesetzt&amp;quot; und &amp;quot;wenn nicht gesetzt&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Die wohl wichtigsten sind die Möglichkeiten, die sich aus dem &amp;quot;ZERO&amp;quot;- und dem &amp;quot;CARRY&amp;quot;-Flag ergeben:&lt;br /&gt;
 BREQ Zieladresse   ' Verzweigen, wenn &amp;quot;GLEICH&amp;quot;  (equal)                      Zero  = 1&lt;br /&gt;
 BRNE Zieladresse   ' Verzweigen, wenn &amp;quot;NICHT GLEICH&amp;quot;  (not equal)            Zero  = 0&lt;br /&gt;
 BRLO Zieladresse   ' Verzweigen, wenn &amp;quot;KLEINER&amp;quot;  (lower)                     Carry = 1&lt;br /&gt;
 BRSH Zieladresse   ' Verzweigen, wenn &amp;quot;GLEICH ODER GRÖSSER&amp;quot; (same or higher) Carry = 0&lt;br /&gt;
Und, die Überraschung, ausgerechnet sowas Häufiges wie &lt;br /&gt;
 Verzweigen, wenn &amp;quot;GRÖSSER&amp;quot;&lt;br /&gt;
gibt's überhaupt nicht. Nun, dazu müßten ja eigentlich zwei Flags abgefragt werden. &amp;quot;Größer&amp;quot; heißt nämlich CARRY = 0 UND ZERO = 0. Und das ist in der &amp;quot;RISC&amp;quot; Welt nicht drin, da wird gespart.&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
Lieber gleich ein Beispiel zum Ausprobieren und Festigen, das war ja doch etwas gebündelt. Aber davor gleich noch eins drauf: Eine &amp;quot;Zieladresse&amp;quot; ist der (im ganzen Programm) eindeutige Name eines Befehls (ein &amp;quot;Label&amp;quot;), der in der Zeile ganz links beginnt und mit Doppelpunkt abgeschlossen wird&lt;br /&gt;
&lt;br /&gt;
====Flußdiagramm====&lt;br /&gt;
*Theoretisch sieht das ja so aus:&lt;br /&gt;
[[Bild:Compare1.png]]&lt;br /&gt;
&lt;br /&gt;
*Da es aber keiner Programmierspache möglich ist, alternativen Code nebeneinander zu schreiben, muß dieser Teil auf &amp;quot;Spaghetti&amp;quot;-Code umstrukturiert werden. &lt;br /&gt;
&lt;br /&gt;
&amp;quot;Hochsprachen&amp;quot; machen das versteckt im Maschinencode, beim Assembler müssen wir selbst machen. Und natürlich auch &amp;quot;GOTO&amp;quot; (=JMP) verwenden, ein sonst in allen Büchern als &amp;quot;no, no&amp;quot; (=pfui) beschriebener Befehl.  &lt;br /&gt;
====Die Praxis====&lt;br /&gt;
&lt;br /&gt;
[[Bild:Compare2.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Programm_Beginn:                            ' das ist zum Beispiel gleich ein &amp;quot;Label&amp;quot;&lt;br /&gt;
          LDI         R25, 12       ' R25 = 12&lt;br /&gt;
          LDI         R24, 44       ' R24 = 44&lt;br /&gt;
'--------------------------------------&lt;br /&gt;
'  nun der Vergleich   &lt;br /&gt;
'--------------------------------------&lt;br /&gt;
          CP          R25, R24       &lt;br /&gt;
          BREQ        Label_1    ' Verzweigen nach &amp;quot;Ziel&amp;quot;, wenn R25 = R24&lt;br /&gt;
&lt;br /&gt;
          LDI         R16, 1      ' das machen wir (zum Beispiel), wenn R25 NICHT= r24 ist &lt;br /&gt;
          RJMP        Label_2    'wir müssen unbedingt springen, sonst laufen wir ja &lt;br /&gt;
                                         ' in den Zweig &amp;quot;ist_gleich&amp;quot;  rein&lt;br /&gt;
Label_1:&lt;br /&gt;
          LDI         R16, 0      ' das machen wir (zum Beispiel), wenn R25 = r24 ist &lt;br /&gt;
&lt;br /&gt;
'----------------------------     ' da treffen wir uns wieder&lt;br /&gt;
Label_2:            &lt;br /&gt;
         da geht er wieder gemeinsam weiter &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ich kann nur dringend empfehlen, sich mit diesem Beispiel zu beschäftigen und auch mit anderen Werten rumzuprobieren, das &amp;quot;bedingte Verzweigen&amp;quot; in allen Varianten ist das A und O der Programmiererei, beim Assembler eben auch ein bißchen verschärft.&lt;br /&gt;
&lt;br /&gt;
====Eine Alternative: Bedingtes &amp;quot;Skip&amp;quot;====&lt;br /&gt;
Was der AVR noch anbietet, ist eine Reihe von &amp;quot;SKIP IF&amp;quot; Befehlen. Für unseren Registervergleich gibt es aber nur den &lt;br /&gt;
 CPSE Register, Register &lt;br /&gt;
Befehl. Er bedeutet:&lt;br /&gt;
 &amp;quot;Vergleiche die Register, und wenn die Inhalte gleich sind, überspringe den nächsten Befehl&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Das wird uns das Herumspringen und das Verwenden von Labeln erspart. Allerdings kann immer nur EIN Befehl übersprungen werden&lt;br /&gt;
&lt;br /&gt;
[[Bild:skip.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
eine Besonderheit hat der Befehl noch: Da er ja Vergleich und Bedingungsabfrage in Einem ist, werden auch keine Flags im Statusregister (SREG) verändert. Das ist praktisch, wenn man diese Flags durch eine andere Operation vorher gesetzt hat, und sie über diesen Vergleichs + Sprung - Befehl  darüber-retten will. Das ist aber im Moment schon etwas fortgeschritten.&lt;br /&gt;
&lt;br /&gt;
==Kurze Zusammenfassung==&lt;br /&gt;
*Wir können also beliebige Register mit beliebigen Werten laden, &lt;br /&gt;
*Wir können mit diesen Werten rechnen oder sie vergleichen&lt;br /&gt;
*Und je nach Vergleichs- oder Rechenergebnis unterschiedlichen Code durchlaufen. &lt;br /&gt;
&lt;br /&gt;
*Man könnte aber auch ein paar Lehren daraus ziehen: &lt;br /&gt;
**die Register R16 - R31 braucht man unter Umständen für Zwischenschritte, um Werte in die Register R0 - R15 laden zu können. Man sollte also diese Register nicht zu schnell fest belegen und vollräumen, damit man dafür noch Spielraum behält.&lt;br /&gt;
**Auch doch recht simple IF .. ELSE Konstrukte können ein gewisses vorher überlegtes Konzept brauchen, sonst verliert man schnell den Überblick. Ein Blatt Papier und ein Bleistift sind also recht hilfreich. Assembler schreibt man nicht einfach in den Bildschirm rein.&lt;br /&gt;
&lt;br /&gt;
==Schleifen==&lt;br /&gt;
Eigentlich ist das ja nichts speziell Assembler-spezifisches, aber was soll's. &lt;br /&gt;
===Flußdiagramme===&lt;br /&gt;
Es gibt zwei Grundmuster für Schleifen (Befehlswiederholungen).&lt;br /&gt;
&lt;br /&gt;
*WHILE  &amp;quot;solange ''Bedingung'' erfüllt ist, mache was&amp;quot;&lt;br /&gt;
[[Bild:While.png]]&lt;br /&gt;
&lt;br /&gt;
*DO...LOOP WHILE  &amp;quot;mache was, solange ''Bedingung'' erfüllt ist&amp;quot;&lt;br /&gt;
[[Bild:DoWhile.png]]&lt;br /&gt;
&lt;br /&gt;
Der Unterschied ist wichtig: Bei &amp;quot;WHILE&amp;quot; wird nur was gemacht, wenn die Bedingung schon zutrifft, Bei &amp;quot;DO..WHILE&amp;quot; werden die Befehle auf jeden Fall wenigstens einmal ausgeführt, erst dann wird gecheckt, ob wiederholt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Praxis===&lt;br /&gt;
Theoretisch sieht das ja gut aus, und mit Hochsprachen kann man das auch meist so formulieren. Beim Assembler geht das aber nur so schön übersichtlich, wenn man nur eine einzelne Bedingung hat. Eine einfache Zähl-Schleife in der &amp;quot;WHILE&amp;quot; Version:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$asm&lt;br /&gt;
   LDI   r25, 0         ' R25 = 0&lt;br /&gt;
   LDI   r24, 1         ' R24 = 1&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12           ' Der Befehl ist neu: vergleiche R25 mit dem festen Wert &amp;quot;12&amp;quot; &lt;br /&gt;
   BREQ  SchleifenAusgang  ' Wenn R25 = 12, verlassen wir die Schleife&lt;br /&gt;
   ADD   R25, R24          ' auf R25 den Wert von R24 draufaddieren &lt;br /&gt;
   RJMP  SchleifenBeginn   ' und wieder rauf zur Prüfung&lt;br /&gt;
SchleifenAusgang:&lt;br /&gt;
   ...&lt;br /&gt;
$end Asm&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Was geschieht, ist klar: R25 beginnt mit Null. Wenn der R25 NICHT= &amp;quot;12&amp;quot;, addieren wir &amp;quot;1&amp;quot; auf R25 und wiederholen das Ganze. Wenn R25 = &amp;quot;12&amp;quot;, verlassen wir die Schleife. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nehmen wir aber an, wir hätten zwei Bedingungen (es geht hier nicht um Sinn oder Unsinn der Abfrage):&lt;br /&gt;
*WHILE R25 NICHT= &amp;quot;12  UND R24 = &amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12           ' Der Befehl ist neu: vergleiche R25 mit dem festen Wert &amp;quot;12&amp;quot; &lt;br /&gt;
   BREQ  SchleifenAusgang  ' Wenn R25 = 12, verlassen wir die Schleife&lt;br /&gt;
   CPI   R24, 1            ' s.o&lt;br /&gt;
   BRNE  SchleifenAusgang  ' Wenn R24 NICHT= 1, verlassen wir die Schleife&lt;br /&gt;
   ADD   R25, R24          ' auf R25 den Wert von R24 draufaddieren &lt;br /&gt;
   RJMP  SchleifenBeginn   ' und wieder rauf zur Prüfung&lt;br /&gt;
SchleifenAusgang:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*WHILE R25 NICHT= &amp;quot;12  ODER R24 &amp;lt; R25&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 &lt;br /&gt;
SchleifenBody:&lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  SchleifenBody&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Wenn wir da nicht im Kommentar dazuschreiben, worum es geht, kennt sich ein Fremder erst nach einiger Überlegung aus.&lt;br /&gt;
&lt;br /&gt;
===Tips===&lt;br /&gt;
Mehrere Bedingungen in eine UND-ODER Beziehung sind immer fehleranfällig und leicht unübersichtlich&lt;br /&gt;
&lt;br /&gt;
*Als Erstes immer die RICHTIGE (und am besten verständliche) Lösung suchen, und erst dann durch Umformungen die &amp;quot;SCHÖNE&amp;quot; Lösung. &lt;br /&gt;
&lt;br /&gt;
*Also nochmal das obige &amp;quot;ODER&amp;quot; Beispiel, erst in der vollen Grundform&lt;br /&gt;
&lt;br /&gt;
 WHILE  ( R25 NICHT= &amp;quot;12 ) ODER  ( R24 &amp;lt; R25 )&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12           ' R25 &amp;lt;=&amp;gt; 12&lt;br /&gt;
   BREQ  R25_ist_12&lt;br /&gt;
   JMP   R25_ist_nicht_12&lt;br /&gt;
&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   ADD   R25, R24          ' der &amp;quot;BODY&amp;quot; steht ja fest &lt;br /&gt;
   RJMP  SchleifenBeginn   ' das ist auch sicher&lt;br /&gt;
&lt;br /&gt;
SchleifenAusgang:          ' ausgang gibt es (eigentlich) immer&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das fehlt was ? Ja, denn jetzt erst sollten wir die Ziele auch hinschreiben&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1. Wir machen den &amp;quot;body&amp;quot; immer, wenn   r25 nicht gleich 12&lt;br /&gt;
&lt;br /&gt;
also schreiben wir das hin&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12           ' &lt;br /&gt;
   BREQ  R25_ist_12&lt;br /&gt;
   JMP   R25_ist_nicht_12  ' abgehakt&lt;br /&gt;
&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25&lt;br /&gt;
&lt;br /&gt;
R25_ist_nicht_12:          ' &lt;br /&gt;
   ADD   R25, R24          ' &lt;br /&gt;
   RJMP  SchleifenBeginn   ' &lt;br /&gt;
&lt;br /&gt;
SchleifenAusgang:          ' &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Wir machen den &amp;quot;body&amp;quot; immer, wenn   r24 kleiner als r25&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12&lt;br /&gt;
   JMP   R25_ist_nicht_12           ' abgehakt&lt;br /&gt;
&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        ' abgehakt&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25  ' &lt;br /&gt;
&lt;br /&gt;
r24_ist_kleiner_r25:&lt;br /&gt;
R25_ist_nicht_12:           &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn    &lt;br /&gt;
&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Anmerkung: wir können an der selben Stelle beliebig viele Label vergeben&lt;br /&gt;
&lt;br /&gt;
3. Was ist, wenn r25 = 12 ?  dann müssen wir die zweite Bedingung prüfen (ist ja ein ODER) &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 ' abgehakt&lt;br /&gt;
   JMP   R25_ist_nicht_12           ' abgehakt&lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        ' abgehakt&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25  ' &lt;br /&gt;
&lt;br /&gt;
r24_ist_kleiner_r25:&lt;br /&gt;
R25_ist_nicht_12:           &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn    &lt;br /&gt;
&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
4. Bleibt nurmehr &amp;quot;r24 ist nicht kleiner r25&amp;quot;.  Da geht's offenbar dann hin, wenn KEINE der Bedingungen erfüllt ist, also: raus aus der Schleife&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 ' abgehakt &lt;br /&gt;
   JMP   R25_ist_nicht_12           ' abgehakt &lt;br /&gt;
&lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        ' abgehakt&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25  ' abgehakt&lt;br /&gt;
&lt;br /&gt;
r24_ist_kleiner_r25:&lt;br /&gt;
R25_ist_nicht_12:           &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
  &lt;br /&gt;
r24_ist_nicht_kleiner_r25:&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt ist das Ganze zwar nicht elegant, aber richtig und leicht nachvollziehbar.&lt;br /&gt;
&lt;br /&gt;
Wenn der Sprungbefehl und das Ziel unmittelbar hintereinander stehen, können wir uns den Sprung sparen. Also bauen wir etwas um, damit das auch so ist:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 '&lt;br /&gt;
   JMP   R25_ist_nicht_12           ' steht jetzt direkt dahinter&lt;br /&gt;
&lt;br /&gt;
r24_ist_kleiner_r25:                'den ganzen Block raufgeschoben &lt;br /&gt;
R25_ist_nicht_12:           &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
&lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        '&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25  ' steht jetzt direkt dahinter&lt;br /&gt;
 &lt;br /&gt;
r24_ist_nicht_kleiner_r25:&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und kürzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 &lt;br /&gt;
r24_ist_kleiner_r25:      &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        &lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ob das nun ein &amp;quot;WHILE&amp;quot; oder ein &amp;quot;DO..WHILE&amp;quot;  wird, hängt nurmehr davon ab, wo wir zu Beginn in die Befehlsfolge reinspringen. &lt;br /&gt;
*Von oben weg, wie es dort steht, ist es eine &amp;quot;WHILE&amp;quot; Schleife&lt;br /&gt;
*Eine &amp;quot;DO..WHILE&amp;quot; Schleife (Bedingung am Ende prüfen) wird es, wenn wir zuerst mit dem &amp;quot;Body&amp;quot; beginnen. Also &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   JMP   r24_ist_kleiner_r25     ' Erst die Aktion, DANN die Bedingung prüfen &lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 &lt;br /&gt;
r24_ist_kleiner_r25:      &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        &lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist doch praktisch ? &lt;br /&gt;
&lt;br /&gt;
Assembler-mäßig ist das nun ok und erträglich. Aber mit dem theoretischen WHILE-Flußdiagramm hat das nun nicht mehr viel gemeinsam.&lt;br /&gt;
&lt;br /&gt;
==Weg vom Simulator auf den µC==&lt;br /&gt;
Jetzt wird's Zeit, die ersten Schritte in die AVR-Realität zu machen, immer Simulator ist ja langweilig, fast so, als würden wir im Trockenen schwimmen lernen müssen. &lt;br /&gt;
&lt;br /&gt;
Bis jetzt haben wir vom Bascom ja nur die äussere Programmhülle verwendet, jetzt soll er doch auch wirklich was tun. &lt;br /&gt;
&lt;br /&gt;
===Datenaustausch mit BasCom===&lt;br /&gt;
Das funktioniert über Datenfelder, die wir irgendwie definieren müssen. Weil's so einfach ist, lassen wir das erstmal Bascom übernehmen. Das ist kein Rückschritt auf dem Weg zum Assemblerprogrammierer, denn das Definieren von Daten ist ja nichts anderes, als Felderen im SRAM (also im eigentlichen Arbeitsspeicher) Namen zuzuweisen. Über den Namen kann man diese Felder dann auch im Assembler ansprechen. &lt;br /&gt;
&lt;br /&gt;
*'''Bascom--&amp;gt;Assembler'''&lt;br /&gt;
Damit Bascom ein Feld für uns definiert, sagen wir einfach (aber außerhalb der &amp;quot;$asm&amp;quot; / &amp;quot;$end asm&amp;quot; Bereiches) &lt;br /&gt;
 DIM Meinfeld AS BYTE &lt;br /&gt;
Im Bascom, das weiß der Leser vielleicht ja schon, kann man da Werte reinschreiben, so wie wir das mit dem &amp;quot;LDI&amp;quot; Befehl bei Registern gemacht haben&lt;br /&gt;
  Meinfeld = 85 &lt;br /&gt;
Jetzt steht an irgendeiner Speicherstelle (egal wo, wir sprechen es eh' nur über den Namen an) der Binärwert von 85  (Hexadezimal &amp;amp;H55 oder in Bits &amp;amp;B01010101).&lt;br /&gt;
Dieses Feld können wir aber nun auch mit dem Assembler lesen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte           ' definition&lt;br /&gt;
&lt;br /&gt;
   Meinfeld = 85               'Wertzuweisung&lt;br /&gt;
&lt;br /&gt;
   $asm&lt;br /&gt;
   lds   r24, {Meinfeld}        '(das sind zwei geschwungene Klammern)&lt;br /&gt;
                                ' das ist wieder ein neuer Befehl: &amp;quot;LDS&amp;quot;&lt;br /&gt;
   $end Asm&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;LDS&amp;quot; lädt in ein beliebiges Register den Wert von der Speicherstelle, die &amp;quot;Meinfeld&amp;quot; heißt. &lt;br /&gt;
&lt;br /&gt;
*'''Assembler--&amp;gt;Bascom'''&lt;br /&gt;
Das geht auch umgekehrt. Wenn wir den Bascomteil noch etwas vervollständigen&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000               ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                     ' die Baudrate für das Terminal&lt;br /&gt;
                                 ' dadurch macht Bascom alle Einstellungen, die wir für das &lt;br /&gt;
                                 ' Terminal brauchen&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte      ' definition&lt;br /&gt;
&lt;br /&gt;
   $asm&lt;br /&gt;
   LDI   R24, 85&lt;br /&gt;
   STS   {Meinfeld}, R24  ' &amp;quot;STS&amp;quot; ist das Gegenstück zu &amp;quot;LDS&amp;quot;, also vom Register zum Speicher&lt;br /&gt;
   $end Asm&lt;br /&gt;
&lt;br /&gt;
   PRINT  str(Meinfeld)   ' Und schon gibt uns Bascom den Wert (dezimal) auf dem Terminal aus&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Das ist der Vorteil den wir haben, wenn wir Assembler mit Bascom anfangen und nicht mit einem &amp;quot;richtigen&amp;quot; Assembler wie das AVR-Studio. Denn das Gefummel mit der Terminalausgabe erledigt alles Bascom, sonst müßten wir es selbst erst wo abschreiben oder lernen, bis wir auch nur einen Pieps auf dem Terminal sehen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Wenn es interessiert: Das, was wir zuletzt im Assembler geschrieben haben, ist auch genau das, was Bascom an Maschinencode produziert, wenn wir &lt;br /&gt;
 Meinfeld = 85&lt;br /&gt;
hinschreiben&lt;br /&gt;
&lt;br /&gt;
*'''Bascom--&amp;gt;Assembler--&amp;gt;Bascom'''&lt;br /&gt;
Noch immer können wir nur mit fest einprogrammierten Werten arbeiten, d.h. für was anderes als &amp;quot;85&amp;quot; müssen wir ändern, übersetzen und brennen.&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun versuchen, etwas über das Terminal einzugeben, bearbeiten und dann wieder ausgeben. &lt;br /&gt;
&lt;br /&gt;
'''Flußdiagramm'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:InOut.png]]&lt;br /&gt;
&lt;br /&gt;
Den Input lassen wir Bascom übernehmen. INKEY() holt einen Wert von der Tastatur. Er wartet aber nicht, bis was gedrückt wird, sondern gibt einfach NULL aus, wenn nix da ist. &lt;br /&gt;
Wir arbeiten also nur etwas, wenn ein Wert &amp;gt; NULL da ist. &lt;br /&gt;
&lt;br /&gt;
*Zuerst übernehmen wir nur den Part &amp;quot;Bearbeitung&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000               ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                     ' die Baudrate für das Terminal&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte             &lt;br /&gt;
DO&lt;br /&gt;
   Meinfeld = INKEY()            '&amp;quot;EINGABE&amp;quot;&lt;br /&gt;
   IF Meinfeld &amp;lt;&amp;gt; 0 THEN         '&amp;quot;BEDINGUNG&amp;quot;&lt;br /&gt;
       $asm                      '&amp;quot;BEARBEITUNG&amp;quot;&lt;br /&gt;
       lds   r24, {Meinfeld}&lt;br /&gt;
       '-------- Da kommt dann Code rein&lt;br /&gt;
       STS   {Meinfeld}, R24  &lt;br /&gt;
       $end Asm&lt;br /&gt;
       PRINT  str(Meinfeld)      '&amp;quot;AUSGABE&amp;quot;&lt;br /&gt;
   END IF&lt;br /&gt;
&lt;br /&gt;
   LOOP                          'Wiederholung&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, wie das Beispiel jetzt ist, ändern wir aber nichts wirklich, sondern schauen mal, was passiert.&lt;br /&gt;
&lt;br /&gt;
Und es passiert Merkwürdiges:&lt;br /&gt;
&lt;br /&gt;
Wenn wir das laufen lassen und in vielleicht gewohnter Manier &amp;quot;85&amp;quot; tippen und &amp;lt;ENTER&amp;gt; drücken, erscheint auf dem Terminal &lt;br /&gt;
 56&lt;br /&gt;
 53&lt;br /&gt;
 13&lt;br /&gt;
Einerseits ist es ja klar, wenn wir drei Tasten drücken, kriegen wir auch dreimal was zurück, aber wie bekommen wir dann in EIN Byte EINE Zahl &amp;quot;85&amp;quot; ? &lt;br /&gt;
&lt;br /&gt;
Also ein kurzer Side-Step.&lt;br /&gt;
&lt;br /&gt;
==Das Byte und seine vielen Bedeutungen==&lt;br /&gt;
&lt;br /&gt;
Nach wie vor ist klar: Ein Byte besteht aus 8 Bit, die in 256 Möglichkeiten kombiniert werden können. &lt;br /&gt;
&lt;br /&gt;
Das sagt aber nicht aus, was irgendeine dieser Bit-Kombinationen eigentlich '''bedeutet'''.&lt;br /&gt;
&lt;br /&gt;
*Das Byte als Bit-Container&lt;br /&gt;
Da hat jedes Bit seine eigene Bedeutung, unabhängig von den anderen. Also einfach &amp;quot;Schalter&amp;quot; und/oder JA-Nein Anzeigen. &lt;br /&gt;
&lt;br /&gt;
Typisch: '''Status-Register (SREG)'''.&lt;br /&gt;
&lt;br /&gt;
*Das Byte als (binär) Zahl. Hier repräsentieren die 256 Möglichkeiten wirklich die Zahlen von 0-255. Mit diesen Zahlen kann nach den binären Regeln gerechnet werden. &lt;br /&gt;
*Das Byte als zwei '''BCD'''Zahlen. Da besteht das Byte eigentlich aus zweimal 4 Bit (&amp;quot;Nibbles&amp;quot;). Jedes Nibble stellt die Zahlen 0 - 9 dar. Ist inzwischen eine recht exotische Verwendung, überhaupt als &amp;quot;gepacktes&amp;quot; Format, wo in einem Nibble auch noch ein Vorzeichen reinkodiert wurde. &lt;br /&gt;
*Das Byte als (binär) Zahl mit Vorzeichen. Das ist eine Kombination von Zahl und Schalter. Das Bit 2^^7 zeigt das Vorzeichen an. Solange es NULL ist, können die restlichen 7 Bit für Zahlen von (+) 0 - 127 normal verwendet werden. Ist das Vorzeichenbit = &amp;quot;1&amp;quot;, gelten die anderen Bit als Negativ-Zahl. Und sie werden als 2- Komplement dargestellt, d.h. 11111111 = -1. Dadurch geht das Bereich von -128 (10000000) bis +127 (01111111).&lt;br /&gt;
*Das Byte als Zahl, die Zahl ist aber ein Tabellenwert in einer standardisierten Tabelle.  &lt;br /&gt;
Typisch: '''ASCII und seine Varianten'''. Was heißt das ? Nun, gespeichert und hin-und hergeschickt wird zum Beispiel 01000001, das bedeutet aber nicht &amp;amp;H41 oder dezimal 65, sondern das, was in der ASCII-Tabelle an der Stelle 65 steht, und das ist ein großes &amp;quot;A&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Ich werd' mich hüten, hier eine ASCII-Tablle reinzustellen, die gibt es im Internet in Massen. Festgelegt sind die Werte 0 - 127. Wichtig ist nur die Gruppierung:&lt;br /&gt;
*0-31 sind &amp;quot;Steuerzeichen&amp;quot; für die Kommunikation. (&amp;quot;13&amp;quot; stellt das berühmte &amp;lt;ENTER&amp;gt; dar)&lt;br /&gt;
*32 bezeichnet eine Leerstelle (&amp;quot;blank&amp;quot;)&lt;br /&gt;
*48 - 57  für die Ziffern 0 - 9&lt;br /&gt;
*65 - 90  Großbuchstaben &amp;quot;A&amp;quot; bis &amp;quot;Z&amp;quot;&lt;br /&gt;
*97 - 122 Kleinbuchstaben &amp;quot;a&amp;quot; bis &amp;quot;z&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Numerischer Input vom Terminal==&lt;br /&gt;
Nachdem sich offenbar unterscheidet, was wir direkt vom Terminal bekommen und was wir davon brauchen können, brauchen wir ein zweites Datenfeld. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000               ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                     ' die Baudrate für das Terminal&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte             ' das bekommen wir vom Terminal&lt;br /&gt;
Dim Meinezahl As Byte            ' das soll die eigentliche Zahl werden&lt;br /&gt;
&lt;br /&gt;
DO&lt;br /&gt;
   Meinfeld = INKEY()            '&amp;quot;EINGABE&amp;quot;&lt;br /&gt;
   IF Meinfeld &amp;lt;&amp;gt; 0 THEN         '&amp;quot;BEDINGUNG&amp;quot;&lt;br /&gt;
       $asm                      '&amp;quot;BEARBEITUNG&amp;quot;&lt;br /&gt;
       '....   CODE .... (s.u.)&lt;br /&gt;
       $end Asm&lt;br /&gt;
       PRINT  str(Meinezahl)     'die zeigt er nach jeder Taste an&lt;br /&gt;
   END IF&lt;br /&gt;
   LOOP                          'Wiederholung&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Soweit das Gerüst bzw. der Rahmen. In &amp;quot;Meinfeld&amp;quot; stellt der Bascom rein, was getippt wurde und wir müssen das verarbeiten und stellen das jeweilige Ergebnis nach &amp;quot;Meinezahl&amp;quot; rein. &lt;br /&gt;
&lt;br /&gt;
Was ist zu tun ? &lt;br /&gt;
*Prüfen, was getippt wurde.&lt;br /&gt;
*davon abhängig &lt;br /&gt;
**Meinfeld=13 (&amp;lt;ENTER&amp;gt;). Die bisher bekommene Zahl ist komplett, jetzt könnten wir mit ihr irgendetwas rechnen oder sonstwas tun. &lt;br /&gt;
**Meinfeld= 48-57 ('0'-'9'). &lt;br /&gt;
***Das, was bisher in &amp;quot;Meinezahl&amp;quot; steht, multiplizieren wir mit 10, an der Einerstelle steht nun auf jeden Fall einen NULL, die bisherigen Zahlen links davon.&lt;br /&gt;
***Und dann müssen wir das, was reingekommen ist, auf binär 0-9 umwandeln und draufaddieren. &lt;br /&gt;
**Alles Andere ignorieren wir einfach.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000                                          ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                                                ' die Baudrate für das Terminal&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte&lt;br /&gt;
Dim Meinezahl As Byte&lt;br /&gt;
Do&lt;br /&gt;
   Meinfeld = Inkey()                                       '&amp;quot;EINGABE&amp;quot;&lt;br /&gt;
   If Meinfeld &amp;lt;&amp;gt; 0 Then                                    '&amp;quot;BEDINGUNG&amp;quot;&lt;br /&gt;
       $asm                                                 '&amp;quot;BEARBEITUNG&amp;quot;&lt;br /&gt;
       lds     r22, {Meinezahl}                             'die bisherige Zahl&lt;br /&gt;
       lds     r24, {Meinfeld}                              'die neue Ziffer&lt;br /&gt;
       cpi     r24, 13                                      '&amp;lt;ENTER&amp;gt; ?&lt;br /&gt;
       brne    Is_numerisch&lt;br /&gt;
       ' ENTER Gedrückt&lt;br /&gt;
       ' Zahl verarbeiten&lt;br /&gt;
       ' dann löschen für ein Neues&lt;br /&gt;
       clr   r22&lt;br /&gt;
       rjmp    Fertig&lt;br /&gt;
Is_numerisch:&lt;br /&gt;
       cpi     r24, 48                                      '48 = '0'&lt;br /&gt;
       brlo    Fertig                                       'keine Ziffer--&amp;gt;ignorieren&lt;br /&gt;
       cpi     r24, 57 + 1                                  '57 = '9'&lt;br /&gt;
       brsh    Fertig                                       'keine Ziffer--&amp;gt;ignorieren&lt;br /&gt;
Einfügen:&lt;br /&gt;
' Bisherige Zahl * 10   Methode:  n * 10 =&amp;gt; n (8 + 2) =&amp;gt; n * 8 + n * 2&lt;br /&gt;
       lsl     r22                                          ' (n * 2)&lt;br /&gt;
       mov     r23, r22                                     ' kopieren&lt;br /&gt;
       lsl     r23                                          ' (n * 2) * 2&lt;br /&gt;
       lsl     r23                                          ' (n * 2 * 2) * 2&lt;br /&gt;
       add     r22, r23                                     'n * 8 + n * 2&lt;br /&gt;
' neue Ziffer umwandeln&lt;br /&gt;
       andi    r24, 15&lt;br /&gt;
' und addieren&lt;br /&gt;
       add     r22, r24&lt;br /&gt;
Fertig:&lt;br /&gt;
       STS   {Meinezahl}, R22&lt;br /&gt;
       $end Asm&lt;br /&gt;
       Print Str(meinezahl)                                 '&amp;quot;AUSGABE&amp;quot;&lt;br /&gt;
   End If&lt;br /&gt;
   Loop                                                     'Wiederholung&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Da sind ein paar Sachen dabei, die hatten wir noch nicht&lt;br /&gt;
&lt;br /&gt;
*Ich beginne mal von hinten: Bei &amp;quot;Fertig&amp;quot; wird das Register R22 nach &amp;quot;Meinezahl&amp;quot; geschrieben, Bascom zeigt es dann her. Wir müssen also sehen, das in R22 immer das Richtige drin steht.&lt;br /&gt;
*Also, jetzt wieder von vorn, wir holen den bisherigen Wert von &amp;quot;Meinezahl&amp;quot; gleich mal ins R22.&lt;br /&gt;
(Das ist am Anfang einfach NULL)&lt;br /&gt;
*Dann die neue Eingabe in R24&lt;br /&gt;
*Die vergleichen wir mir &amp;quot;13&amp;quot; == &amp;lt;ENTER&amp;gt;&lt;br /&gt;
**ist es eine andere Eingabe (BRNE), schauen wir bei &amp;quot;Is_numerisch&amp;quot; weiter&lt;br /&gt;
**Wenn aber ja, könnten wir jetzt was tun. Wir machen aber weiter nichts, löschen aber R22 und damit &amp;quot;Meinezahl&amp;quot; (s.o) auf NULL, damit wir für eine neue Eingabe bereit sind.&lt;br /&gt;
*Is_numerisch: &lt;br /&gt;
**Die Eingabe ist kleiner als 48 ('0'), keine Ziffer, wir sind auch schon fertig&lt;br /&gt;
**Jetzt müßten wir vergleichen, ob die Eingabe größer als 57 ('9') ist. Das geht nicht [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User#Verzweigen|(siehe weiter oben bei den Verzweigungen)]], also vergleichen wir mit 57+1, bei gleich oder größer ist die Eingabe auch keine Ziffer, also --&amp;gt; fertig.&lt;br /&gt;
*Einfügen: &lt;br /&gt;
**Bisheriger Wert * 10: Bei manchen µC gibt es einen Multiplikationsbefehl, aber diese einfache Rechnung machen wir zu Fuß, ist einfach mehr Spaß und mehr neue Befehle. &lt;br /&gt;
&lt;br /&gt;
n * 10 kann man ja zerlegen in n*(8+2). Und sowohl 8 als auch 2 sind ja 2-er Potenzen, da kann einfach Bit-weise nach links schieben.&lt;br /&gt;
 &amp;quot;LSL  R22&amp;quot;  ist gleich  R22 * 2   &amp;quot;LSL  Logical Shift Left&amp;quot;&lt;br /&gt;
damit haben wir schon mal Meinezahl * 2 gerechnet. Das kopieren wir nach R23 und schieben dort noch zweimal nach links. In R23 steht nun also insgesamt Meinezahl * 8. Das addieren wir nun.  &lt;br /&gt;
*Umwandeln: Die Ziffern '0'-'9', also 48 - 57 schauen hexadezimal ja so aus:&lt;br /&gt;
 0x30 = dezimal 48 = ASCII '0' bis &lt;br /&gt;
 0x39 = dezimal 57 = ASCII '9'&lt;br /&gt;
Wir brauchen also nur die '3' irgendwie zu löschen, dann haben wir sofort unsere 0-9. Das geschieht durch &lt;br /&gt;
 ANDI     R24, 15          ' &amp;quot;AND&amp;quot; mit einer Konstanten &lt;br /&gt;
In R24 bleiben nur die 1-er übrig, wo auch in '15' ein 1-er ist. Damit ist die '3' weg.&lt;br /&gt;
*Addieren&lt;br /&gt;
 ADD  R22, r24           'Meinezahl + neuerWert&lt;br /&gt;
und fertig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine Anmerkung: Wir haben noch keine Prüfung, ob die Zahl für ein Byte nicht zu groß wird. Wenn wir also 9999 eintippen, kommt Schrott heraus.&lt;br /&gt;
&lt;br /&gt;
==Mehr als ein Byte==&lt;br /&gt;
Mit einzelnen Bytes geht es ja jetzt schon recht gut. Aber es gibt ja auch größere Zahlen. &lt;br /&gt;
===16 Bit Zahlen===&lt;br /&gt;
Das nächstgrößere ist eine 16-Bit Zahl. Bei Bascom ist das dann ein &lt;br /&gt;
*WORD   für den Zahlenbereich 0 - 65535 und &lt;br /&gt;
*INTEGER für -32768 bis +32767&lt;br /&gt;
&lt;br /&gt;
Beides wird in zwei hintereinanderliegenden Bytes gespeichert, also  &lt;br /&gt;
 MeinByte und &lt;br /&gt;
 MeinByte + 1&lt;br /&gt;
Und zwar so, daß erst das niederwertigere und dann das höherwertige Byte gespeichert werden. &lt;br /&gt;
Also die 16-Bit Binärzahl  &lt;br /&gt;
 0001001000110100 = Hexadezimal &amp;amp;H1234 &lt;br /&gt;
steht im Speicher in zwei Bytes als&lt;br /&gt;
 00110100 00010010 =  &amp;amp;H34 &amp;amp;H12&lt;br /&gt;
&lt;br /&gt;
Genauso ist es mit den Registern, üblicherweise steht das erste Byte in einem geradzahligen Register und das zweite im nächsthöherem. z.B.:&lt;br /&gt;
 R24: 00110100 =  &amp;amp;H34&lt;br /&gt;
 R25: 00010010 =  &amp;amp;H12&lt;br /&gt;
Sowas nennt man dann &amp;quot;Register-Paar&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Der AVR µC kann einige seiner 32 Register als Registerpaar verwenden. Das sind &lt;br /&gt;
 R0:R1       (aber nur bei den Multiplikationsbefehlen für das Ergebnis)&lt;br /&gt;
 R24:R25     (für 16-Bit Berechnungen)&lt;br /&gt;
 R26:R27     (für 16-Bit Berechnungen und als &amp;quot;Pointer&amp;quot; X)&lt;br /&gt;
 R28:R29     (für 16-Bit Berechnungen und als &amp;quot;Pointer&amp;quot; Y)&lt;br /&gt;
 R30:R31     (für 16-Bit Berechnungen und als &amp;quot;Pointer&amp;quot; Z)&lt;br /&gt;
Wobei es aber nur zwei solche Berechnungen gibt&lt;br /&gt;
 ADIW     R24 , zahl       ' addieren zahl auf r24:r25&lt;br /&gt;
 SBIW     R24 , zahl       ' subtrahieren zahl von r24:r25&lt;br /&gt;
Die anderen 16-Bit Befehle sind eher Adress-Arithmetik. &lt;br /&gt;
&lt;br /&gt;
*Datenaustausch Bascom &amp;lt;=&amp;gt; Assembler&lt;br /&gt;
Um ein WORD (oder INTEGER) mit Bascom auszutauschen, geht z.B. folgendes&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 DIM word_1 AS WORD&lt;br /&gt;
     $asm&lt;br /&gt;
     LDS      R24, {word_1}    'holen Byte 1  (bits 0-7)&lt;br /&gt;
     LDS      R25, {word_1+1}  'holen Byte 2  (bits 8-15)&lt;br /&gt;
     ADIW     R24, 42          'addieren von 42 auf das Paar r24:r25&lt;br /&gt;
     STS      {word_1}, R24    'speichern Byte 1&lt;br /&gt;
     STS      {word_1+1}, R25  'speichern Byte 2&lt;br /&gt;
     $end asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Arithmetik mit 16 Bit &lt;br /&gt;
Der Unterschied zur 8-Bit Verarbeitung ist eigentlich nicht groß. Es gibt zu allen Arithmetik Befehlen des AVR eine &amp;quot;Carry&amp;quot;-Variante, die einen Übertrag von der Aktion vorher berücksichtigt. &lt;br /&gt;
&lt;br /&gt;
Angenommen zwei 16-Bit Werte in den Registerpaaren R24:R25 und R22:R23&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ADD    R24, R22           'Addieren der niederwertigen Bytes&lt;br /&gt;
 ADC    R25, R23           'Addieren der höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
&lt;br /&gt;
 SUB    R24, R22           'Subtrahieren der niederwertigen Bytes&lt;br /&gt;
 SBC    R25, R23           'Subtrahieren der höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Vergleichen mit 16 Bit &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 CP     R24, R22           'Vergleich der niederwertigen Bytes&lt;br /&gt;
 CPC    R25, R23           'Vergleich der höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
           ' jetzt können die normalen &amp;quot;bedingte Verzweigung&amp;quot;-Befehle verwendet werden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===32 Bit Zahlen===&lt;br /&gt;
Bei Bascom heissen die LONG und die sind immer Vorzeichenbehaftet. Im AVR Instructionset gibt es keine 32-Bit Befehle. Die physische Speicherung ist wie bei den 16-Bit Feldern&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 DIM long_1 AS LONG&lt;br /&gt;
     $asm&lt;br /&gt;
     LDS      R24, {long_1}    'holen Byte 1  (bits 0-7)&lt;br /&gt;
     LDS      R25, {long_1+1}  'holen Byte 2  (bits 8-15)&lt;br /&gt;
     LDS      R26, {long_1+2}  'holen Byte 3  (bits 16-23)&lt;br /&gt;
     LDS      R27, {long_1+3}  'holen Byte 4  (bits 24-31)&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Arithmetik mit 32 Bit &lt;br /&gt;
Eigentlich läuft das ab 16-Bit immer gleich ab, und man kann genauso 24-Bit wie 40 oder 48 Bit rechnen. &lt;br /&gt;
Angenommen zwei 32-Bit Werte in den Registern  R16-&amp;gt;R19 und R20-&amp;gt;R23&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ADD    R16, R20           'Addieren der niederwertigen Bytes&lt;br /&gt;
 ADC    R17, R21           'Addieren des nächst-höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
 ADC    R18, R22           'Addieren des nächst-höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
 ADC    R19, R23           'Addieren des nächst-höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Single und Double Zahlen===&lt;br /&gt;
das sind 32- bzw. 64-Bitzahlen, allerdings haben die eine eigene Float-Struktur. Das ist dann schon ein eigenes Thema, mit denen was zu machen. &lt;br /&gt;
&lt;br /&gt;
===Strings===&lt;br /&gt;
Strings sind Bytefolgen, in denen ASCII-Zeichen einfach von links nach rechts gespeichert werden können. Das Ende-Kennzeichen der immer variabel langen Texte ist ein NULL-Byte. Daher braucht in Bascom ein String für (max) 20 Zeichen lange Strings auch in Wirklichkeit 21 Byte Speicher.&lt;br /&gt;
&lt;br /&gt;
Strings sind gut geeignet, eine andere Art vorzustellen, wie man Daten lesen oder speichern kann: Über POINTER-Register&lt;br /&gt;
&lt;br /&gt;
Gleich ganz in die Praxis: Wir wollen die Länge eines Bascom-Strings festellen, natürlich im Assembler&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000                                          ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                                                ' die Baudrate für das Terminal&lt;br /&gt;
&lt;br /&gt;
Dim Text As String * 20                                     ' das ist der String&lt;br /&gt;
Dim Strlen As Byte                                          ' da sol die Länge rein&lt;br /&gt;
&lt;br /&gt;
      Text = &amp;quot;Hello, world !&amp;quot;                               ' wir befüllen den String&lt;br /&gt;
&lt;br /&gt;
      $asm&lt;br /&gt;
      Loadadr Text , X            '&amp;quot;adresse von Text nach R26:r27&amp;quot; &lt;br /&gt;
      clr      r24                'löschen vom Zähler&lt;br /&gt;
Schleife: &lt;br /&gt;
      ld       r22, X+            ' s.u.&lt;br /&gt;
      cpi      r22, 0             'Vergleichen mit NULL&lt;br /&gt;
      breq     Fertig             'fertig&lt;br /&gt;
      inc      r24                'Zähler erhöhen&lt;br /&gt;
      rjmp     Schleife           'weiter&lt;br /&gt;
Fertig:&lt;br /&gt;
      sts   {strlen}, r24         'ergebnis ablegen&lt;br /&gt;
      $end Asm&lt;br /&gt;
&lt;br /&gt;
      Print Str(strlen)           'herzeigen&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*'''Loadadr''' ist ein Bascom-Statement, mit dem das Registerpaar R26:R27 mit der '''Adresse''' von &amp;quot;Text&amp;quot; geladen wird. In einem &amp;quot;echten&amp;quot; Assembler kann man das auch assemblermäßiger formulieren, aber in Bascom muß man das so machen&lt;br /&gt;
*'''LD'''&lt;br /&gt;
      ld       r22, X+        'Das Zeichen, wo X hinzeigt, nach R22 und X um eins erhöhen&lt;br /&gt;
Den Rest kennen wir eigentlich schon.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''ST''' ist übrigens das Gegenstück zu LD&lt;br /&gt;
      st       X+, r22        'R22 dorhin, wo X hinzeigt, und dann X um eins erhöhen&lt;br /&gt;
&lt;br /&gt;
==Bascom als Messlatte==&lt;br /&gt;
Eine sehr gute Möglichkeite, Assembler zu trainieren, ist es, irgendwelche Bascom-Statements durch eigene Inline-Assembler-Blöcke zu ersetzen. Dabei kann man immer auch Varianten ausprobieren. &lt;br /&gt;
*Beispiel&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DIM  Wert1 AS WORD&lt;br /&gt;
DIM  Wert2 AS WORD&lt;br /&gt;
        Wert1 = Wert2   ' Bascom-Statement&lt;br /&gt;
&lt;br /&gt;
'als Inline-Assembler  EINE Möglichkeit&lt;br /&gt;
        $asm&lt;br /&gt;
        LDS     R24, {Wert1}&lt;br /&gt;
        STS     {Wert2}, R24&lt;br /&gt;
        LDS     R24, {Wert1+1}&lt;br /&gt;
        STS     {Wert2+1}, R24&lt;br /&gt;
        $end asm&lt;br /&gt;
'als Inline-Assembler  ANDERE Möglichkeit&lt;br /&gt;
        $asm&lt;br /&gt;
        LOADADR Wert1, X&lt;br /&gt;
        LD     R24, X+&lt;br /&gt;
        LD     R25, X+&lt;br /&gt;
        LOADADR Wert2, X&lt;br /&gt;
        ST     X+, R24&lt;br /&gt;
        ST     X+, R25&lt;br /&gt;
        $end asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bascom_Inside-Code|Es gibt hier ein paar Beispiele]], wie Bascom dieses oder jenes in Maschinencode umsetzt, dabei ist es auch sehr lehrreich, erstmal zu überlegen, wie man es selbst machen würde, und dann erst nachzusehen, wie es Bacom gelöst hat.&lt;br /&gt;
&lt;br /&gt;
==ALU und Status-Register (SREG)==&lt;br /&gt;
Bisher haben wir nur von Zero-Bits und &amp;quot;Oberflow&amp;quot; bzw. Carry-Bit gesprochen und uns da ein wenig drübergeschwindelt. Es ist ja auch tatsächlich so, daß man mit dem bisher erworbenen Wissen ganze Legionen von Robotern programmieren kann. &lt;br /&gt;
&lt;br /&gt;
Aber ganz kommt man an den diversen Flags im Statusregister ja doch nicht vorbei, schon garnicht, wenn man ernsthafte Berechnungen anstellen will.  &lt;br /&gt;
&lt;br /&gt;
Zwei Bit im SREG brauchen wir an dieser Stelle aber nicht weiter zu beachten&lt;br /&gt;
*T-Bit oder Transfer-Bit. Das dient für einige Befehle als &amp;quot;Zwischendepot&amp;quot; für einzelne Bits&lt;br /&gt;
*Global Interrupt Enable-Bit. Wie der Name sagt, werden damit einfach alle Interrupts zugelassen oder eben nicht. Von Interrupts haben wir aber noch garnicht gesprochen &lt;br /&gt;
*Die andern Bits dienen der ALU (Arithmetic-Logical-Unit) als Ergebnis-Anzeige bei den ganzen arithmetisch-logischen Befehlen&lt;br /&gt;
===ALU (Recheneinheit)=== &lt;br /&gt;
Immer, wenn zwei Bytes zu vermangeln sind, geht das mit der Recheneinheit. Die hat 4 Byte zur Verfügung:&lt;br /&gt;
*Input-Byte A&lt;br /&gt;
*Input-Byte B&lt;br /&gt;
*Output-Byte R&lt;br /&gt;
*Statusregister SREG&lt;br /&gt;
(Daß er dabei Input A gleichzeitig als Output verwendet, ignorieren wir mal einfach, das verwirrt nur und ändert nix grundsätzliches)&lt;br /&gt;
&lt;br /&gt;
====Ein-Bit-Volladdierer==== &lt;br /&gt;
Wir wissen ja alle noch, wie das mit dem Addieren im 2-er System funktioniert ?  &lt;br /&gt;
 A   B    R&lt;br /&gt;
 0 + 0  = 0&lt;br /&gt;
 0 + 1  = 1&lt;br /&gt;
 1 + 0  = 1&lt;br /&gt;
 1 + 1  = 0  und Überlauf&lt;br /&gt;
Diesen Überlauf muß man nun an das nächsthöhere Bit weitergeben. Andererseits kommt von dem niedrigeren Bit ja auch ein Überlauf daher. Also sieht das für ein Bit dann so aus&lt;br /&gt;
[[Bild:ALU1.png]]&lt;br /&gt;
&lt;br /&gt;
====Mit 8-Bit==== &lt;br /&gt;
Für alle 8 Bit wird es etwas größer, ein paar direkte Status-Bit hab ich gleich eingezeichnet&lt;br /&gt;
[[Bild:ALU2.png]]&lt;br /&gt;
*Der Schalter &amp;quot;mit od. ohne Carry&amp;quot; ist da, ob man einen vorherigen Überlauf miteinbeziehen will &lt;br /&gt;
 ADD   register, register         OHNE Carry &lt;br /&gt;
 ADC   register, register         MIT  Carry&lt;br /&gt;
*N-Bit:  Ist einfach der Wert (0 oder 1), den das Bit 2^^7 im Ergebnis hat&lt;br /&gt;
*Z-Bit:  Steht im Ergebnis alles auf 0, wird das gesetzt&lt;br /&gt;
*Half-Carry: Das wird gesetzt, wenn es vom Bit 2^^3 einen Überlauf in Richtung 2^^4 gegeben hat. (Das braucht man, wenn man mit BCD-Zahlen, also nur von 0-9 rechnen will. Dazu später, wenn Interesse da ist)&lt;br /&gt;
*Carry-Bit: Ist nun klar, das ist einfach der Überlauf vom 2^^7 Bit.&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;S&amp;quot; und &amp;quot;V&amp;quot; Bit====&lt;br /&gt;
=====8 Bit mit Vorzeichen=====&lt;br /&gt;
Kurze Wiederholung *gähn*, wie das mit den Vorzeichen ist: Man &amp;quot;spiegelt&amp;quot; die Zahlen an der Null-Stelle. Nehmen wir an, wir haben grad eine NULL im Byte. Ziehen wir jetzt 1 ab, soll ja offenbar -1 rauskommen. Was steht im Byte tatsächlich drin ? Richtig, Hexadezimal FF. Und genauso schaut auch -1 eben aus. Alle diese negativen Zahlen haben gemeinsam, daß zumindest das Bit 2^^7 eine 1 zeigt. Daran erkennt man, daß die Zahl negativ ist. Das funktioniert aber nur so bis -128 (hex 80) und +127 (hex 7F). &lt;br /&gt;
&lt;br /&gt;
Negative Zahlen werden also dargestellt als &amp;quot;2-er Komplement&amp;quot;. &lt;br /&gt;
*Man dreht 0-er und 1-er im Byte um&lt;br /&gt;
*und addiert &amp;quot;1&amp;quot; drauf. &lt;br /&gt;
           0b01111000 hex 78 = dezimal 120&lt;br /&gt;
 umdrehen: 0b10000111 hex 87  &lt;br /&gt;
 +1      : 0b10001000 hex 88  Das stellt jetzt -120 dar.&lt;br /&gt;
&lt;br /&gt;
=====Rechnen mit vorzeichenbehafteten 8 Bit=====&lt;br /&gt;
Der Sinn und Zweck des &amp;quot;S&amp;quot; und &amp;quot;V&amp;quot; Bit ist nun leicht verständlich. &lt;br /&gt;
*Bisher wurde ja addiert&lt;br /&gt;
     0b01111000 hex 78 = dezimal 120&lt;br /&gt;
   + 0b00001010 hex 0A = dezimal  10&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82 = dezimal 130       &lt;br /&gt;
*Addiert wird jetzt immer noch genauso, aber jetzt hat das Bit 2^^7 eine andere Bedeutung, weil es das Vorzeichen darstellt&lt;br /&gt;
*Ein Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0b10000000 hex 80 = dezimal -128&lt;br /&gt;
   + 0b00000010 hex 02 = dezimal   +2&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82  (Vorzeichenbit ist gesetzt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vorzeichenbit gesetzt-&amp;gt; hex 82 ist also ein zweier-Komplement, und damit ist es -126.&lt;br /&gt;
&lt;br /&gt;
*Aber nun:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0b01111000 hex 78 = dezimal +120&lt;br /&gt;
   + 0b00001010 hex 0A = dezimal  +10&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82  (Vorzeichenbit ist gesetzt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vorzeichenbit gesetzt-&amp;gt; hex 82 ist also ein zweier-Komplement, und damit ist es -126.&lt;br /&gt;
&lt;br /&gt;
'''Bitte ???'''&lt;br /&gt;
&lt;br /&gt;
Das kann ja wohl nicht sein, es muß ja +130 rauskommen. &lt;br /&gt;
&lt;br /&gt;
Das Carry-Bit hilft nichts, das ist immer NULL geblieben. &lt;br /&gt;
&lt;br /&gt;
*Genau da hilft das &amp;quot;V&amp;quot; Bit&lt;br /&gt;
&lt;br /&gt;
[[Bild:ALU3.png]]&lt;br /&gt;
&lt;br /&gt;
Das V-Bit = 1, &lt;br /&gt;
*wenn entweder die beiden Input-Bytes positiv waren (2^^7=0) UND das Ergebnis aber 2^^7=1 zeigt. &lt;br /&gt;
*wenn beiden Input-Bytes negativ waren (2^^7=1) UND das Ergebnis aber 2^^7=0 zeigt. &lt;br /&gt;
&lt;br /&gt;
Im Prinzip sagt also das V-Bit aus, ob das Vorzeichenbit im Ergebnis durch einen Überlauf zustandegekommen ist. &lt;br /&gt;
&lt;br /&gt;
*Und das &amp;quot;S&amp;quot;-Bit ? Das kombiniert das Vorzeichen im Ergebnis mit diesem &amp;quot;V&amp;quot;-Bit.&lt;br /&gt;
**S=1, wenn das Vorzeichenbit im Ergebnis zwar NULL ist, aber nur, weil ein Überlauf stattgefunden hat. Das Ergebnis ist also trotzdem negativ und stellt ein 2-er Komplement dar.  &lt;br /&gt;
**S=1, wenn das Vorzeichenbit im Ergebnis EINS ist, und zwar OHNE, daß ein Überlauf stattgefunden hätte. Das Ergebnis ist also korrekt negativ und stellt auch ein 2-er Komplement dar&lt;br /&gt;
&lt;br /&gt;
Ob an das nächsthöhere Byte ein Überlauf weiterzugeben ist, sagt bei Vorzeichen-Bytes nun das &amp;quot;V&amp;quot; Bit.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nochmal die obigen Vorzeichen-Beispiele zum mitsingen, aber diesmal mit den N, V und S Flags &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0b10000000 hex 80 = dezimal -128&lt;br /&gt;
   + 0b00000010 hex 02 = dezimal   +2&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82   N=1 V=0 S=1    Ist also negativ, 2-er Kompl. kein Überlauf --&amp;gt;  -126&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     0b01111000 hex 78 = dezimal +120&lt;br /&gt;
   + 0b00001010 hex 0A = dezimal  +10&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82   N=1 V=1 S=0    Ist also positiv, 2^^7 löschen, der Rest bleibt &lt;br /&gt;
                                        (dezimal 2), hat aber Überlauf, der ist +128 wert, &lt;br /&gt;
                                        insgesamt also --&amp;gt; 128 + 2 = +130&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Mehr als 8 Bit mit Vorzeichen=====&lt;br /&gt;
Keine Sorge, das Vorzeichenbit gibt es bei allen binärzahlen immer nur einmal, an der höchsten Stelle. Wir haben also auch bei eine 8-Byte / 64-Bit Variablen nur ein einziges Mal das Problem.&lt;br /&gt;
&lt;br /&gt;
*Das Vorzeichenbit ist immer das MSB (höchst aussagekräftige Bit = most significant Bit). Zwei 32 Bit sieht das so aus:&lt;br /&gt;
 0b00000000000000000000000111111111 = hex 000001FF = dezimal +511&lt;br /&gt;
 0b11111111111111111111110000000001 = hex FFFFFC01 = dezimal -1023&lt;br /&gt;
Das Vorzeichenbit ist das ganz links (2^^31) &lt;br /&gt;
&lt;br /&gt;
Addiert werden solche Zahlen erstmal ganz normal &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dim Vala As Long&lt;br /&gt;
Dim Valb As Long&lt;br /&gt;
Dim Stat As Byte&lt;br /&gt;
&lt;br /&gt;
      Vala = 511&lt;br /&gt;
      Valb = -1023&lt;br /&gt;
      Print Hex(vala) ; &amp;quot;+&amp;quot; ; Hex(valb) ; &amp;quot;=&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
      $asm&lt;br /&gt;
      lds      r16, {vala}&lt;br /&gt;
      lds      r17, {vala+1}&lt;br /&gt;
      lds      r18, {vala+2}&lt;br /&gt;
      lds      r19, {vala+3}&lt;br /&gt;
&lt;br /&gt;
      lds      r20, {valb}&lt;br /&gt;
      lds      r21, {valb+1}&lt;br /&gt;
      lds      r22, {valb+2}&lt;br /&gt;
      lds      r23, {valb+3}&lt;br /&gt;
&lt;br /&gt;
      add      r16, r20            'das erste Byte noch ohne Carry&lt;br /&gt;
      adc      r17, r21            'der Rest mit Carry&lt;br /&gt;
      adc      r18, r22&lt;br /&gt;
      adc      r19, r23&lt;br /&gt;
&lt;br /&gt;
      in       r20, sreg           ' Wir holen uns die Status-Bits&lt;br /&gt;
      sts      {stat}, r20         ' speichern&lt;br /&gt;
&lt;br /&gt;
      sts      {Vala}, r16         ' speichern Ergebnis&lt;br /&gt;
      sts      {Vala+1}, r17&lt;br /&gt;
      sts      {Vala+2}, r18&lt;br /&gt;
      sts      {Vala+3}, r19&lt;br /&gt;
&lt;br /&gt;
      $end Asm&lt;br /&gt;
      Print Hex(vala) ; &amp;quot; &amp;quot; ; Bin(stat) ; &amp;quot; &amp;quot; ; Str(vala)            ' Zum Anschauen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Wir sehen, gesetzt wird von der ALU letzlich: &lt;br /&gt;
 S=1 und N=1&lt;br /&gt;
Also ist die Zahl negativ (S=1) und muß als 2-er Komplement verstanden werden.&lt;br /&gt;
&lt;br /&gt;
===Das Carry-Bit beim Bit-Verschieben===&lt;br /&gt;
Häufig wird das Carry-Bit auch als Zwischenlager und Anzeige bei den Schiebe- und Rotationsbefehlen. &lt;br /&gt;
&lt;br /&gt;
Es verhält sich da wie ein 9.Bit zu den 8-Bits im Register. &lt;br /&gt;
&lt;br /&gt;
Die Links- und Rechts-Schiebebefehle sind völlig symmetrisch&lt;br /&gt;
====LSL &amp;amp; ROL====&lt;br /&gt;
[[Bild:Left.png]]&lt;br /&gt;
 LSL register &lt;br /&gt;
 ROL register&lt;br /&gt;
Die Bits im Register werden bei beiden Befehlen eine Stelle nach links verschoben, das höchste Bit wandert in das Carry-Bit. &lt;br /&gt;
*Bei LSL wird eine NULL in das niederwertigste Bit des Registers reingeschoben&lt;br /&gt;
*Bei ROL wird das Carry-Bit (von vorher) in dieses Bit gestellt.&lt;br /&gt;
&lt;br /&gt;
====LSR &amp;amp; ROR====&lt;br /&gt;
[[Bild:Right.png]]&lt;br /&gt;
 LSR register &lt;br /&gt;
 ROR register&lt;br /&gt;
Die Bits im Register werden bei beiden Befehlen eine Stelle nach rechts verschoben, das niederste Bit wandert in das Carry-Bit. &lt;br /&gt;
*Bei LSR wird eine NULL in das höchste Bit des Registers reingeschoben&lt;br /&gt;
*Bei ROR wird das Carry-Bit (von vorher) in dieses Bit gestellt.&lt;br /&gt;
&lt;br /&gt;
====Anwendungen====&lt;br /&gt;
*Multiplizieren mit 2, 4, 8,... &lt;br /&gt;
Bei einem Byte ist da wohl nicht viel dazu zu sagen. &lt;br /&gt;
 LDI   register, &amp;amp;H05   ' register = &amp;amp;H05   = dezimal 5&lt;br /&gt;
 LSL   register         ' register = &amp;amp;H0A   = dezimal 10&lt;br /&gt;
Bei mehreren Bytes, also Variablen mit mehr als 8 Bit, beginnt man immer mit dem niederwertigsten Byte, der erste Befehl OHNE Carry, damit nicht irgendetwas ungewolltes reinrutscht, und dann weiter mit den anderen Bytes&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  LDS  register, {long_variable }    ' einlesen&lt;br /&gt;
  LSL  register                      ' verschieben, 2^^7 kommt ins Carry, bleibt&lt;br /&gt;
                                     ' 0 kommt nach 2^^0&lt;br /&gt;
  STS  {long_variable }, register    ' speichern (ändert nix im SREG)&lt;br /&gt;
&lt;br /&gt;
  LDS  register, {long_variable +1 }    ' einlesen (ändert auch nix im SREG)&lt;br /&gt;
  ROL  register                         ' verschieben, &lt;br /&gt;
                                        ' carry von vorher nach 2^^0&lt;br /&gt;
                                        ' 2^^7 ins Carry, bleibt&lt;br /&gt;
  STS  {long_variable + 1}, register    ' speichern &lt;br /&gt;
    .usw. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Dividieren durch 2, 4, 8,... &lt;br /&gt;
Da ist eben alles genau umgekehrt. Beginnen mit dem höchsten, sonst wie gehabt &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  LDS  register, {long_variable + 3}    &lt;br /&gt;
  LSR  register                         ' verschieben, 2^^0 kommt ins Carry und bleibt&lt;br /&gt;
                                        ' 0 kommt nach 2^^7&lt;br /&gt;
  STS  {long_variable +3}, register       &lt;br /&gt;
&lt;br /&gt;
  LDS  register, {long_variable + 2 }    &lt;br /&gt;
  ROR  register                         ' verschieben, &lt;br /&gt;
                                        ' carry von vorher nach 2^^7&lt;br /&gt;
                                        ' 2^^0 ins Carry&lt;br /&gt;
  STS  {long_variable + 2}, register    &lt;br /&gt;
    .usw. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Anwendung Lauflicht====&lt;br /&gt;
Ist auch beliebt. Angenommen, wir haben auf PORTB 8 LED hängen und möchten da ein Lauflicht haben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $asm&lt;br /&gt;
    LDI    r24, &amp;amp;HFF&lt;br /&gt;
    !OUT   DDRB, R24           ' wir setzen das Port auf OUTPUT&lt;br /&gt;
    LDI    r16, &amp;amp;H00           ' das &amp;quot;SchiebeByte&amp;quot; auf NULL&lt;br /&gt;
    SEC                        ' wir setzen das Carry Bit, damit ein 1-er da ist &lt;br /&gt;
Dauerschleife:&lt;br /&gt;
    ROL    r16                 ' Beim ersten Mal rutscht das carry-Bit rein&lt;br /&gt;
                               ' danach geht der 1-er automatisch im Kreis&lt;br /&gt;
    !OUT   PORTB, R16          ' wir legen das Byte mit dem einen 1-er an das Port&lt;br /&gt;
                               '---------------------------------------------------&lt;br /&gt;
                               ' Hier muß aber eine Verzögerung rein, sonst ist das &lt;br /&gt;
                               ' viel zu schnell&lt;br /&gt;
                               '---------------------------------------------------&lt;br /&gt;
    RJMP   Dauerschleife       ' und immer weiter&lt;br /&gt;
    $end asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Schreiben wir&lt;br /&gt;
     ROR    r16&lt;br /&gt;
geht's in die andere Richtung&lt;br /&gt;
&lt;br /&gt;
==Ecken, Kanten und Tücken==&lt;br /&gt;
Das AVR-Instruction-Set weist einige Feinheiten auf, die schon manchen Assembler-Roboter auf Kollisionskurs gebracht haben. &lt;br /&gt;
&lt;br /&gt;
Grad' dieses Kapitel kann ich nur nach und nach auffüllen, weil mir auch nicht immer gleich alles so einfällt. Vielleicht gibt's aber auch ein paar andere Assembleure, die ihren Erfahrungsschatz hier einbringen. &lt;br /&gt;
&lt;br /&gt;
Ich kann nur raten, beim Programmieren immer zumindest das &amp;quot;Complete Instruction Set Summary&amp;quot; bei der Hand zu haben. Immer wieder nachsehen, ob ein gerade ausgewählter Befehl das macht, was man sich vorstellt, und vor allem, ob er auch alle die Status-Register-Flags so setzt oder löscht, die man braucht. &lt;br /&gt;
&lt;br /&gt;
===INC/DEC===&lt;br /&gt;
Für Schleifenzähler etc. werden ja gerne &amp;quot;INC&amp;quot; (register +1) oder &amp;quot;DEC&amp;quot; (register -1) verwendet. Mit einem Vergleich auf einen Wert kontrolliert man dann diese Schleife. Ist ja auch ok. &lt;br /&gt;
&lt;br /&gt;
Muß man den Schleifenzähler aber nun so erweitern, daß man mehr als ein Byte braucht, muß man aufpassen. &lt;br /&gt;
&lt;br /&gt;
'''INC od. DEC   kümmern sich NICHT um das Carry-Bit !'''  &lt;br /&gt;
&lt;br /&gt;
*wer also schreibt&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 clr r0        (manche nehmen das gerne als Register, in dem immer NULL steht. Ist praktisch)&lt;br /&gt;
 ....&lt;br /&gt;
 INC r16&lt;br /&gt;
 adc r17, r0 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
kann sein blaues Wunder erleben. &lt;br /&gt;
&lt;br /&gt;
Da INC bei einem Überlauf einfach wieder bei NULL weitermacht, das Carry-Bit aber nicht anrührt, addieren wir im &amp;quot;ADC&amp;quot; Befehl ein Carry-Bit dazu, das von irgendeinem Befehl vorher übriggeblieben ist.&lt;br /&gt;
&lt;br /&gt;
Für ein sauberes Carry-Bit muß man also auf einen &amp;quot;echten&amp;quot; Additionsbefehl umsteigen. Und was sehen wir ?&lt;br /&gt;
&lt;br /&gt;
'''Es gibt keinen &amp;quot;ADDI&amp;quot; Befehl'''  ( für sowas wie &amp;quot;addi register, 1&amp;quot; )&lt;br /&gt;
&lt;br /&gt;
Wenn wir also kein Register zur Hand haben, in das wir einen 1-er reinschreiben können, müssen wir&lt;br /&gt;
 SUBI register, -1 &lt;br /&gt;
schreiben. Ist klar:  register - (-1) = register + 1, also das, was wir brauchen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
 SUBI    r16, -1        &lt;br /&gt;
 SBCI    r17, -1            (abziehen festen Wert von Register MIT CARRY) &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wen die vielen &amp;quot;-1&amp;quot; wundern: Wir brauchen für eine 16 Bit Zahl (R16:R17) natürlich auch ein 16-Bit-tiges &amp;quot;-1&amp;quot; Literal. Und bei den Vorzeichen-Zahlen haben wir ja festgestellt, daß -1 ja so aussieht:&lt;br /&gt;
 bei  8-Bit 0b11111111                          &amp;amp;HFF&lt;br /&gt;
 bei 16-Bit 0b1111111111111111                  &amp;amp;HFFFF&lt;br /&gt;
 bei 32-Bit 0b11111111111111111111111111111111  &amp;amp;HFFFFFFFF&lt;br /&gt;
Und diese vielen Bits müssen wir auf die einzelnen Subtraktionsbefehlt eben verteilen &lt;br /&gt;
&lt;br /&gt;
*Hinaufzählen mit z.B. 32 Bit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
 SUBI    r16, -1        &lt;br /&gt;
 SBCI    r17, -1            &lt;br /&gt;
 SBCI    r18, -1            &lt;br /&gt;
 SBCI    r19, -1            &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Beim Dekrementieren ist eigentlich nur das Literal ein anderes: statt -1 eben +1, d.h., wir subtrahieren &amp;quot;wirklich&amp;quot;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
 SUBI    r16, 1        &lt;br /&gt;
 SBCI    r17, 0    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*32 Bit ? Richtig:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
 SUBI    r16, 1        &lt;br /&gt;
 SBCI    r17, 0            &lt;br /&gt;
 SBCI    r18, 0            &lt;br /&gt;
 SBCI    r19, 0            &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Ja, es gibt aber doch was für 16 Bit ?'''&lt;br /&gt;
 ADIW  register:register+1 ,  nn  ' nn auf die 16-bit von register:register+1 addieren &lt;br /&gt;
 SBIW  register:register+1 ,  nn  ' nn von den 16-bit von register:register+1 subtrahieren&lt;br /&gt;
(''nn'' kann sein 0 - 63)&lt;br /&gt;
&lt;br /&gt;
Ja, aber das geht nur mit den Registerpaaren R24:R25, R26:R27, R28:R29, R30:R31&lt;br /&gt;
&lt;br /&gt;
Da die Paare R26:R27, R28:R29, R30:R31 aber gleichzeitig die einzigen Pointer-Register sind (dazu kommen wir noch, aber grad in Schleifen braucht man die meist für was anderes)&lt;br /&gt;
&lt;br /&gt;
'''Bleibt eigentlich nur R24:R25 für solche 16-Bit Befehle'''&lt;br /&gt;
&lt;br /&gt;
==Unterprogramme==&lt;br /&gt;
Man sieht ja schon an den bisherigen Beispielen, daß gewisse Befehlsfolgen immer wieder benötigt werden. &lt;br /&gt;
*Typisches Beispiel, was man ja schon beim ersten Lauflicht braucht, sind Verzögerungsroutinen, damit die LEDs nicht gar so schnell funzeln. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man überall Zählschleifen einbauen, die auf diese Art das Programm gewissermaßen ausbremsen. Aber das verbraucht dann doch mächtig Platz, und auch nur die geringste Änderung in der Befehlsfolge muß man x-mal machen. &lt;br /&gt;
&lt;br /&gt;
Wenn man also diese Befehlsfolge einmal zusammenfaßt und irgendwo außerhalb der Haupt-Programm-Schleife deponiert und mit einem Namen versieht, könnte man doch immer, wenn man sie braucht, diese Folge aufrufen. Ja, aber wie findet man dann wieder zurück, um dort weiterzumachen, wo man grad war?&lt;br /&gt;
&lt;br /&gt;
*Ganz einfach:&lt;br /&gt;
===CALL &amp;amp; RET===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hauptschleife:&lt;br /&gt;
    .....&lt;br /&gt;
    .....&lt;br /&gt;
    CALL   Befehlsfolge&lt;br /&gt;
    .....&lt;br /&gt;
    CALL   Befehlsfolge&lt;br /&gt;
    .....&lt;br /&gt;
    .....&lt;br /&gt;
    JMP   Hauptschleife&lt;br /&gt;
&lt;br /&gt;
Befehlsfolge:&lt;br /&gt;
    ....&lt;br /&gt;
    RET  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das sind zwei listige Erweiterungen von &amp;quot;JMP&amp;quot;:&lt;br /&gt;
*CALL heißt: Erst merken, wo wir grad sind, und dann JMP irgendwohin&lt;br /&gt;
*RET heißt: Mach &amp;quot;JMP&amp;quot; auf die Stelle, die man sich gemerkt hat (und vergiß sie dann)&lt;br /&gt;
&lt;br /&gt;
Super, kann man gut brauchen, aber wie macht der AVR das ? &lt;br /&gt;
&lt;br /&gt;
Dazu braucht der AVR zwei Dinge:  Einen &amp;quot;Stapel&amp;quot; und einen &amp;quot;Stapelzeiger&amp;quot;. &lt;br /&gt;
*Den &amp;quot;Stapelzeiger&amp;quot; (englisch &amp;quot;Stackpointer&amp;quot;) hat er schon in der CPU eingebaut. &lt;br /&gt;
*Der &amp;quot;Stapel&amp;quot; (&amp;quot;Stack&amp;quot;) ist ein Stück Speicher, den uns Bascom bei seiner Initialisierung vom oberen Ende des SRAM abgeknöpft hat. &lt;br /&gt;
**Am Anfang enthält der Stackpointer die oberste Adresse vom Stack&lt;br /&gt;
**Jedesmal, wenn sich der AVR eine Rücksprungadresse merken soll, schreibt er sie dorthin, wo der Stackpointer grad hinzeigt, und zieht dann 2 (so lange ist eine solche Adresse) vom Stackpointer ab.&lt;br /&gt;
**Geht es jetzt ans &amp;quot;RET&amp;quot;, addiert er jetzt diese 2 wieder auf den Pointer, und springt zu der Adresse, die dort steht. &lt;br /&gt;
**Sagt man nun aber nicht &amp;quot;RET&amp;quot;, sondern nochmal &amp;quot;CALL&amp;quot;, schreibt er diese (neue) Adresse nun UNTER die vorherige (und zieht wieder 2 vom Pointer ab). Im Stack stehen jetzt also beide Adressen untereinander, der Stack ist größer geworden &lt;br /&gt;
&lt;br /&gt;
*Der Speicher ist also ein armer Hund: Mit jedem &amp;quot;CALL&amp;quot; knabbern wir von oben was weg, und mit jedem &amp;quot;DIM&amp;quot; (im Bascom) von unten. &lt;br /&gt;
 Erschütternde Tragödien spielen sich ab, wenn sich der Stack und das &amp;quot;DIM&amp;quot; Bereich &lt;br /&gt;
 mal irgendwo in der Mitte treffen, die beiden Bereiche sind sich nämlich spinnefeind.&lt;br /&gt;
Beim Bascom selbst wird es noch viel früher dramatisch: Der verlangt nämlich, daß wir schon beim Programmieren schätzen, wie groß der Stack wohl werden wird ($HWSTACK=nn).&lt;br /&gt;
&lt;br /&gt;
[[Bascom_Inside#Stacks_.26_Frame|Da gibt's ein paar Details dazu]]&lt;br /&gt;
&lt;br /&gt;
===PUSH &amp;amp; POP===&lt;br /&gt;
Mit diesen Befehlen wird der Stack und der Stackpointer direkt angesprochen. Denn &amp;quot;CALL &amp;amp; RET&amp;quot; sind nicht die einzigen, die das brauchen. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 PUSH  register      'Den Inhalt von register an die Adresse schreiben, an die der Stackpointer&lt;br /&gt;
                     'grad hinzeigt, und dann 1 vom Pointer abziehen&lt;br /&gt;
 POP   register      'Auf den Stackpointer 1 addieren, und das Byte dort ins register laden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Typische Anwendung:&lt;br /&gt;
In unserem &amp;quot;Unterprogramm&amp;quot; müssen wir irgendwelche Register verändern, wir wollen aber nicht, daß das Hauptprogramm, das uns aufgerufen hat, was davon merkt&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Befehlsfolge:&lt;br /&gt;
    PUSH  R24&lt;br /&gt;
    LDI   R24, 127&lt;br /&gt;
_Schleife:&lt;br /&gt;
    DEC   R24&lt;br /&gt;
    BRNE  _Schleife&lt;br /&gt;
    POP   R24&lt;br /&gt;
    RET  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nach dem &amp;quot;RET&amp;quot; hat R24 also wieder den Inhalt, den es vorher hatte.&lt;br /&gt;
==Interrupt-Routinen (ISR)==&lt;br /&gt;
ISR = &amp;quot;Interrupt Service Request&amp;quot; = &amp;quot;Unterbrechungs-Behandlung-Anforderung&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Ein AVR besteht ja nicht nur aus CPU und Speicher, sondern auch aus einer Reihe von &amp;quot;Geräten&amp;quot;, die ihre Funktion völlig unabhängig durchführen, wenn man sie mal konfiguriert hat. &lt;br /&gt;
&lt;br /&gt;
Aber immer, wenn sie grad ihre Meß- oder Zählfunktion erledigt haben, wollen sie ihr Ergebnis irgendwie mitteilen und dafür wissen, wie's weitergehen soll. &lt;br /&gt;
&lt;br /&gt;
Wir können dazu mit dem AVR einen Deal machen: &lt;br /&gt;
*Wir schreiben ein Unterprogramm, daß seine dahingehenden Ansprüche erfüllt und sagen dem AVR, wo es im Programmspeicher steht.&lt;br /&gt;
*Und dafür paßt der AVR auf das &amp;quot;Gerät&amp;quot; auf und macht den &amp;quot;Call&amp;quot; auf dieses Unterprogramm völlig automatisch genau dann, wenn es soweit ist. Wir brauchen uns im &amp;quot;normalen&amp;quot; Programm nun nicht mehr darum zu kümmern&lt;br /&gt;
&lt;br /&gt;
Um so einen Handel einzufädeln, ist es wirklich praktisch, wenn wir Bascom hinzuziehen. Der kennt nämlich die meisten AVR-Controllertypen gewissermaßen persönlich und weiß am besten, an welchen Strippen man da bei irgendeinem Controller ziehen muß. &lt;br /&gt;
===Beispiel Timer===&lt;br /&gt;
Sagen wir, wir wolle jede Millisekunde ein Unterprogramm durchführen (lassen). &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
$hwstack = 48&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kennen wir an sich schon. Aber wenn wir Interrupts verwenden, sollten wir bei &amp;quot;$HWSTACK=&amp;quot; schon ein bißchen was spendieren. &lt;br /&gt;
&lt;br /&gt;
*Jetzt lassen wir Bascom den Timer konfigurieren&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Config Timer0 = Timer , Prescale = 64      'Timer Setup&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lassen wir das Bascom machen. Für uns ist es dann nämlich egal, ob das auf einem Tiny oder einem ATmega128 laufen wird, es schaut immer gleich aus. &lt;br /&gt;
&lt;br /&gt;
*Jetzt der Deal: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   On Timer0 Interrupt_ticker       ' Das ist das Unterprogramm (heißt jetzt aber ISR-Routine) &lt;br /&gt;
&lt;br /&gt;
   Enable Timer0                    ' jetzt machen wir den Timer scharf &lt;br /&gt;
&lt;br /&gt;
   Enable Interrupts                ' Und das ist sozusagen der Hauptschalter für sowas&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Das &amp;quot;normale&amp;quot; Programm: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   $asm&lt;br /&gt;
Hauptschleife:&lt;br /&gt;
   '------------------------------&lt;br /&gt;
   ' Irgendein (Assembler?) Programm, dem der Timer völlig schnuppe ist&lt;br /&gt;
   '------------------------------&lt;br /&gt;
   JMP Hauptschleife &lt;br /&gt;
   $end asm&lt;br /&gt;
&lt;br /&gt;
END&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Und nun die ISR-Routine&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Interrupt_ticker:             &lt;br /&gt;
&lt;br /&gt;
   Timer0 = 131            ' Damit der arme Counter nicht immer von Null weg zählen muß&lt;br /&gt;
                           ' er darf schon mit 131 anfangen&lt;br /&gt;
&lt;br /&gt;
   $asm&lt;br /&gt;
   '------------------------------&lt;br /&gt;
   ' Die befehle, die wir jede mS ausführen wollen &lt;br /&gt;
   '------------------------------&lt;br /&gt;
&lt;br /&gt;
   $end asm&lt;br /&gt;
&lt;br /&gt;
   Return                  ' wieder zurück und weiter im normalen Programm &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die hier verwendeten Werte von &amp;quot;64&amp;quot; für den Prescaler und die &amp;quot;131&amp;quot; für den Counter sind aus der Angabe &amp;quot;$crystal = 8000000&amp;quot; berechnet worden und ergeben Interrupts im Abstand von 1 mS.&lt;br /&gt;
&lt;br /&gt;
==Autor==&lt;br /&gt;
[[User:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
*[[AVR_Assembler_Einf%C3%BChrung|AVR Assembler Einführung]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
*[[Bascom Debounce ISR in Assembler]]&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User&amp;diff=13945</id>
		<title>Assembler Einführung für Bascom-User</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User&amp;diff=13945"/>
				<updated>2008-09-03T20:57:04Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Was hier folgt, ist nichts für Profis und Power-User, die mögen weiterblättern. Ich versuche hier, absolute Neueinsteiger nach und nach mit ein paar Grundinformationen zu versorgen. &lt;br /&gt;
&lt;br /&gt;
==Assembler Einführung für Bascom-User==&lt;br /&gt;
&lt;br /&gt;
===Wieso Bascom ?===&lt;br /&gt;
Eine der einfachsten Möglichkeiten, sich an Assembler heranzutasten, ist es, den Bascom-Compiler als Workbench zu benutzen. &lt;br /&gt;
&lt;br /&gt;
Die Vorteile:&lt;br /&gt;
*Das Drumherum mit der richtigen Initialisierung, auch der Peripherie, kann man bequem von Bascom machen lassen, bis man sich halt auskennt.  &lt;br /&gt;
*Wenn irgendeine Berechnung oder Teil-Funktion nervt oder nicht gleich richtig hinhaut, schreibt man halt doch ein paar Bascom-Statements. &lt;br /&gt;
*fürs Erste reicht die Demo-Version allemal&lt;br /&gt;
&lt;br /&gt;
Die Nachteile:&lt;br /&gt;
*Gott-weiß-wie komfortabel ist der Bascom-Assembler natürlich nicht, aber es reicht. &lt;br /&gt;
*Bei manchen Befehlen ist es nicht klar, ob das ein Assembler oder ein Bascom-Befehl ist. In diesem Fall muß man ein &amp;quot;!&amp;quot; Rufzeichen davor setzen. Man erkennt das aber sofort, denn diese reservierten Bascom-Wort mach er sofort in '''Fettschrift'''. Trotzdem aufpassen !&lt;br /&gt;
&lt;br /&gt;
==Ein Grund-Programm==&lt;br /&gt;
Nicht lachen, auch das ist ein Bascom-Programm:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$asm&lt;br /&gt;
   &lt;br /&gt;
$end Asm&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das Programm macht natürlich überhaupt nix. Aber durch die paar Zeilen hat Bascom alle &lt;br /&gt;
[[AVR_Assembler_Einf%C3%BChrung#Struktur_eines_AVR-Maschinenprogrammes|notwendigen Initialisierungen]] schon erledigt und wir brauchen uns um nichts zu kümmern.&lt;br /&gt;
Zwischen &amp;quot;$asm&amp;quot; und &amp;quot;$end asm&amp;quot; kann man nun nach Herzenslust irgendwas Assemblermäßiges reinschreiben und mit dem Simulator rumprobieren. &lt;br /&gt;
&lt;br /&gt;
Auch &amp;quot;REGFILE&amp;quot; müßte man nicht hinschreiben, dann gilt eben das, was man in &amp;quot;OPTIONS/COMPILER/CHIP&amp;quot; eingestellt hat.&lt;br /&gt;
&lt;br /&gt;
==Der Zentral-Prozessor (CPU)==&lt;br /&gt;
Das ist der Kollege, dem man mit &amp;quot;Assembler-Instruktionen&amp;quot; davon überzeugen muß, irgendwas zu tun. Ohne den läuft garnix. Der hat als Hilfe einen &amp;quot;'''Befehlszähler'''&amp;quot; (PC), der immer auf den nächsten Befehl zeigt, der drankommt. Und dann hat er noch eine Reihe &amp;quot;'''Register'''&amp;quot;, das sind kleine Zwischenspeicher, mit denen er arbeiten kann. Die heissen einfach &amp;quot;R0&amp;quot;, &amp;quot;R1&amp;quot;,....&amp;quot;R31&amp;quot;, also 32 Stück, in jedes paßt genau ein Byte, und ein Byte, das wissen wir, besteht wiederum aus 8 Bits.&lt;br /&gt;
&lt;br /&gt;
[[Atmel_Controller_Mega16_und_Mega32#CPU|Hübsche Zeichnungen von Atmega32-CPU und einige weiterführende Information]] &lt;br /&gt;
&lt;br /&gt;
===Registerverwendung===&lt;br /&gt;
An sich sind wir zwischen &amp;quot;$asm&amp;quot; und &amp;quot;$end asm&amp;quot; alleiniger Herrscher über den Mikrokontroller. Damit aber auch dem Bascom ein bißchen was zu Leben bleibt, sollten wir einige Register entweder in Ruhe lassen oder erst sichern und dann wieder herstellen. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 R4, R5         ' die beiden verwendet Bascom für temporäre Sachen. &lt;br /&gt;
 R6             ' da speichert er einige Schalter (Bits)&lt;br /&gt;
 R8, R9         ' verwendet es für  &amp;quot;READ&amp;quot; und &amp;quot;RESTORE&amp;quot; etc. &lt;br /&gt;
                ' haben wir sowas aber garnicht, ist es egal&lt;br /&gt;
 R28, R29       ' braucht Bascom aber nur, wenn wir Funktionen und Subs aufrufen. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen brauchen wir uns aber darum nicht zu kümmern, es wird nix davon gebraucht. Ich wollt' es nur gesagt haben.&lt;br /&gt;
&lt;br /&gt;
===Daten-Transfer Operationen===&lt;br /&gt;
Bevor wir mit diesen Registern irgendetwas ausprobieren können, müssen wir erstmal gezielt bestimmte Werte reinschreiben können. Sowas heißt eben &amp;quot;Transfer&amp;quot;. Da wir ja erst am Anfang sind, reicht uns zum Beispiel:&lt;br /&gt;
 LDI   R24, 14&lt;br /&gt;
Damit wird in das Register R24 der Binärwert von &amp;quot;14&amp;quot; reingestellt, das sind die Bits &amp;quot;00001110&amp;quot;. Der maximale Wert, da es ja nur ein Byte ist, wäre &amp;quot;255&amp;quot;, also &amp;quot;11111111&amp;quot;. &lt;br /&gt;
Für den Befehl &amp;quot;LDI&amp;quot; können wir übrigens leider nur die Register R16 - R31 setzen, das ist so eine Einschränkung von wegen &amp;quot;RISC&amp;quot; Architektur.&lt;br /&gt;
 MOV   R3, R24&lt;br /&gt;
Deswegen auch der zweite Befehl &amp;quot;MOV&amp;quot;, damit wird im Beispiel der Inhalt von R24 in das Register R3 kopiert. Somit können wir mit maximal zwei Befehlen also jeder beliebige Register von R0 bis R31 mit beliebigen Werten laden. &lt;br /&gt;
Natürlich gibt es noch eine Menge mehr an Transferbefehlen, aber Listen von Assembler-Befehlen gibt es schon genug, da brauchen wir hier nicht auch noch eine.&lt;br /&gt;
&lt;br /&gt;
===Arithmetisch-Logische Operationen===&lt;br /&gt;
Laden wir mal zwei Register:&lt;br /&gt;
 LDI   R25, 17&lt;br /&gt;
 LDI   R24, 14&lt;br /&gt;
&lt;br /&gt;
Und jetzt die Grund-Befehle, Varianten später:&lt;br /&gt;
*Arithmetisch&lt;br /&gt;
 ADD   R25, R24       addieren      R25 + R24, Ergebnis nach R25&lt;br /&gt;
 !SUB   R25, R24       subtrahieren&lt;br /&gt;
*Logisch &lt;br /&gt;
 !AND   R25, R24       &amp;quot;UND&amp;quot;&lt;br /&gt;
 !OR    R25, R24       &amp;quot;ODER&amp;quot;&lt;br /&gt;
 EOR   R25, R24       &amp;quot;Exklusiv-ODER&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis steht immer in Operand-1&lt;br /&gt;
&lt;br /&gt;
===Gleich mal ausprobieren===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$asm&lt;br /&gt;
 LDI   R25, 17        ' Laden&lt;br /&gt;
 LDI   R24, 14        ' Laden&lt;br /&gt;
 ADD   R25, R24       'addieren  17 + 14, Ergebnis in R25&lt;br /&gt;
&lt;br /&gt;
 LDI   R25, 17        'Nachladen, da R25 durch &amp;quot;ADD&amp;quot; ja verändert wurde&lt;br /&gt;
 !SUB   R25, R24       'subtrahieren  17 - 14&lt;br /&gt;
&lt;br /&gt;
 LDI   R25, 17        ' Laden&lt;br /&gt;
 LDI   R24, 14        ' Laden&lt;br /&gt;
 !AND   R25, R24       ' Es kommt überall dort &amp;quot;1&amp;quot; raus, wo sowohl r25 als auch R24 eine 1 haben&lt;br /&gt;
 &lt;br /&gt;
 LDI   R25, 17        ' Laden&lt;br /&gt;
 LDI   R24, 14        ' Laden&lt;br /&gt;
 !OR    R25, R24       ' Es kommt überall dort &amp;quot;1&amp;quot; raus, wo r25 oder R24 eine 1 haben&lt;br /&gt;
                      '  (ODER BEIDE !)&lt;br /&gt;
&lt;br /&gt;
 LDI   R25, 17        ' Laden&lt;br /&gt;
 LDI   R24, 14        ' Laden&lt;br /&gt;
 EOR   R25, R24       ' Es kommt überall dort &amp;quot;1&amp;quot; raus, wo ENTWEDER  r25 oder R24 eine 1 haben&lt;br /&gt;
                      '  (ABER NICHT BEIDE !)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Probieren ist das am besten mit dem Simulator. (Register-Fenster öffnen und Einzelschritte)&lt;br /&gt;
&lt;br /&gt;
===Ergebnis prüfen===&lt;br /&gt;
Normalerweise ist es ja nicht so, daß vor solchen Operationen die Rechenwerte direkt geladen werden, sondern die kommen ja von irgendwo aussen her. Und da muß man ja dann anders reagieren, je nachdem, ob die Werte gleich waren, ob r25 größer oder kleiner als r24 war, und so weiter. &lt;br /&gt;
&lt;br /&gt;
Da helfen die &amp;quot;Flags&amp;quot; im '''Status-Register''' (SREG). Das ist zwar auch ein normales Byte, nur haben die einzelnen Bits darin eine spezielle Bedeutung und geben eben nähere Auskunft über die gerade abgelaufenen Operation. Nur das Wichtigste:&lt;br /&gt;
*ZERO-Bit  Es wird automatisch gesetzt, wenn das Ergebnis genau NULL ergeben hat.  &lt;br /&gt;
*CARRY-Bit Es wird automatisch gesetzt, wenn es einen &amp;quot;Übertrag&amp;quot; gegeben hat&lt;br /&gt;
&lt;br /&gt;
Man kann diese (und noch andere) Flags sehen, wenn man im Simulator auf &amp;quot;µP&amp;quot; drückt. &lt;br /&gt;
 Z = ZERO&lt;br /&gt;
 C = CARRY&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LDI   R25, 17       &lt;br /&gt;
LDI   R24, 14       &lt;br /&gt;
!SUB   R25, R24       &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Zero &amp;amp; Carry sind nicht gesetzt, denn das Ergebnis ist ungleich NULL, und &amp;quot;17&amp;quot; ist außerdem größer als &amp;quot;14&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LDI   R25, 17       &lt;br /&gt;
LDI   R24, 17       &lt;br /&gt;
!SUB   R25, R24       &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt ist Zero gesetzt, denn das Ergebnis ist gleich NULL&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LDI   R25, 12       &lt;br /&gt;
LDI   R24, 44       &lt;br /&gt;
!SUB   R25, R24       &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt ist das Carry-Bit gesetzt, denn &amp;quot;12&amp;quot; ist ja kleiner als &amp;quot;44&amp;quot;, das Ergebnis ist also negativ, und ein &amp;quot;Übertrag&amp;quot; ist auch aufgetreten.&lt;br /&gt;
&lt;br /&gt;
===Vergleichen===&lt;br /&gt;
&amp;quot;Vergleichen&amp;quot; ist für die ALU (Recheneinheit) das Gleiche wie Subtrahieren (SUB), nur daß das eigentliche Rechenergebnis nirgends hingeschrieben wird und NUR DIE FLAGS gesetzt werden. &lt;br /&gt;
 CP  R25, R24       &lt;br /&gt;
&lt;br /&gt;
===Verzweigen===&lt;br /&gt;
Wir haben ja gesagt, es wird verglichen, damit der Rechner je nach Vergleichs- der Rechenergebnis was anderes tut. &amp;quot;Was anderes tun&amp;quot; heißt anderer Code, also muß der &amp;quot;Befehlszähler&amp;quot; einen anderen Wert bekommen, damit der Programmablauf dort fortgesetzt wird. Dazu gibt es natürlich die &amp;quot;unbedingten&amp;quot; Varianten&lt;br /&gt;
 JMP  Zieladresse  ' oder&lt;br /&gt;
 RJMP Zieladresse  ' das nimmt man, wenn das Ziel in der Nähe ist&lt;br /&gt;
Oder eben die &amp;quot;Verzweigung unter bestimmten Bedingungen&amp;quot; (conditional branch) &lt;br /&gt;
 BRxxx Zieladresse &lt;br /&gt;
Für &amp;quot;xxx&amp;quot; (Bedingung) gibt es nun eine ganze Reihe Möglichkeiten. Es gibt im Prinzip für jedes Bit im Status-Register (s.o) eine Abfrage &amp;quot;wenn gesetzt&amp;quot; und &amp;quot;wenn nicht gesetzt&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Die wohl wichtigsten sind die Möglichkeiten, die sich aus dem &amp;quot;ZERO&amp;quot;- und dem &amp;quot;CARRY&amp;quot;-Flag ergeben:&lt;br /&gt;
 BREQ Zieladresse   ' Verzweigen, wenn &amp;quot;GLEICH&amp;quot;  (equal)                      Zero  = 1&lt;br /&gt;
 BRNE Zieladresse   ' Verzweigen, wenn &amp;quot;NICHT GLEICH&amp;quot;  (not equal)            Zero  = 0&lt;br /&gt;
 BRLO Zieladresse   ' Verzweigen, wenn &amp;quot;KLEINER&amp;quot;  (lower)                     Carry = 1&lt;br /&gt;
 BRSH Zieladresse   ' Verzweigen, wenn &amp;quot;GLEICH ODER GRÖSSER&amp;quot; (same or higher) Carry = 0&lt;br /&gt;
Und, die Überraschung, ausgerechnet sowas Häufiges wie &lt;br /&gt;
 Verzweigen, wenn &amp;quot;GRÖSSER&amp;quot;&lt;br /&gt;
gibt's überhaupt nicht. Nun, dazu müßten ja eigentlich zwei Flags abgefragt werden. &amp;quot;Größer&amp;quot; heißt nämlich CARRY = 0 UND ZERO = 0. Und das ist in der &amp;quot;RISC&amp;quot; Welt nicht drin, da wird gespart.&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
Lieber gleich ein Beispiel zum Ausprobieren und Festigen, das war ja doch etwas gebündelt. Aber davor gleich noch eins drauf: Eine &amp;quot;Zieladresse&amp;quot; ist der (im ganzen Programm) eindeutige Name eines Befehls (ein &amp;quot;Label&amp;quot;), der in der Zeile ganz links beginnt und mit Doppelpunkt abgeschlossen wird&lt;br /&gt;
&lt;br /&gt;
====Flußdiagramm====&lt;br /&gt;
*Theoretisch sieht das ja so aus:&lt;br /&gt;
[[Bild:Compare1.png]]&lt;br /&gt;
&lt;br /&gt;
*Da es aber keiner Programmierspache möglich ist, alternativen Code nebeneinander zu schreiben, muß dieser Teil auf &amp;quot;Spaghetti&amp;quot;-Code umstrukturiert werden. &lt;br /&gt;
&lt;br /&gt;
&amp;quot;Hochsprachen&amp;quot; machen das versteckt im Maschinencode, beim Assembler müssen wir selbst machen. Und natürlich auch &amp;quot;GOTO&amp;quot; (=JMP) verwenden, ein sonst in allen Büchern als &amp;quot;no, no&amp;quot; (=pfui) beschriebener Befehl.  &lt;br /&gt;
====Die Praxis====&lt;br /&gt;
&lt;br /&gt;
[[Bild:Compare2.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Programm_Beginn:                            ' das ist zum Beispiel gleich ein &amp;quot;Label&amp;quot;&lt;br /&gt;
          LDI         R25, 12       ' R25 = 12&lt;br /&gt;
          LDI         R24, 44       ' R24 = 44&lt;br /&gt;
'--------------------------------------&lt;br /&gt;
'  nun der Vergleich   &lt;br /&gt;
'--------------------------------------&lt;br /&gt;
          CP          R25, R24       &lt;br /&gt;
          BREQ        Label_1    ' Verzweigen nach &amp;quot;Ziel&amp;quot;, wenn R25 = R24&lt;br /&gt;
&lt;br /&gt;
          LDI         R16, 1      ' das machen wir (zum Beispiel), wenn R25 NICHT= r24 ist &lt;br /&gt;
          RJMP        Label_2    'wir müssen unbedingt springen, sonst laufen wir ja &lt;br /&gt;
                                         ' in den Zweig &amp;quot;ist_gleich&amp;quot;  rein&lt;br /&gt;
Label_1:&lt;br /&gt;
          LDI         R16, 0      ' das machen wir (zum Beispiel), wenn R25 = r24 ist &lt;br /&gt;
&lt;br /&gt;
'----------------------------     ' da treffen wir uns wieder&lt;br /&gt;
Label_2:            &lt;br /&gt;
         da geht er wieder gemeinsam weiter &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ich kann nur dringend empfehlen, sich mit diesem Beispiel zu beschäftigen und auch mit anderen Werten rumzuprobieren, das &amp;quot;bedingte Verzweigen&amp;quot; in allen Varianten ist das A und O der Programmiererei, beim Assembler eben auch ein bißchen verschärft.&lt;br /&gt;
&lt;br /&gt;
====Eine Alternative: Bedingtes &amp;quot;Skip&amp;quot;====&lt;br /&gt;
Was der AVR noch anbietet, ist eine Reihe von &amp;quot;SKIP IF&amp;quot; Befehlen. Für unseren Registervergleich gibt es aber nur den &lt;br /&gt;
 CPSE Register, Register &lt;br /&gt;
Befehl. Er bedeutet:&lt;br /&gt;
 &amp;quot;Vergleiche die Register, und wenn die Inhalte gleich sind, überspringe den nächsten Befehl&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Das wird uns das Herumspringen und das Verwenden von Labeln erspart. Allerdings kann immer nur EIN Befehl übersprungen werden&lt;br /&gt;
&lt;br /&gt;
[[Bild:skip.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
eine Besonderheit hat der Befehl noch: Da er ja Vergleich und Bedingungsabfrage in Einem ist, werden auch keine Flags im Statusregister (SREG) verändert. Das ist praktisch, wenn man diese Flags durch eine andere Operation vorher gesetzt hat, und sie über diesen Vergleichs + Sprung - Befehl  darüber-retten will. Das ist aber im Moment schon etwas fortgeschritten.&lt;br /&gt;
&lt;br /&gt;
==Kurze Zusammenfassung==&lt;br /&gt;
*Wir können also beliebige Register mit beliebigen Werten laden, &lt;br /&gt;
*Wir können mit diesen Werten rechnen oder sie vergleichen&lt;br /&gt;
*Und je nach Vergleichs- oder Rechenergebnis unterschiedlichen Code durchlaufen. &lt;br /&gt;
&lt;br /&gt;
*Man könnte aber auch ein paar Lehren daraus ziehen: &lt;br /&gt;
**die Register R16 - R31 braucht man unter Umständen für Zwischenschritte, um Werte in die Register R0 - R15 laden zu können. Man sollte also diese Register nicht zu schnell fest belegen und vollräumen, damit man dafür noch Spielraum behält.&lt;br /&gt;
**Auch doch recht simple IF .. ELSE Konstrukte können ein gewisses vorher überlegtes Konzept brauchen, sonst verliert man schnell den Überblick. Ein Blatt Papier und ein Bleistift sind also recht hilfreich. Assembler schreibt man nicht einfach in den Bildschirm rein.&lt;br /&gt;
&lt;br /&gt;
==Schleifen==&lt;br /&gt;
Eigentlich ist das ja nichts speziell Assembler-spezifisches, aber was soll's. &lt;br /&gt;
===Flußdiagramme===&lt;br /&gt;
Es gibt zwei Grundmuster für Schleifen (Befehlswiederholungen).&lt;br /&gt;
&lt;br /&gt;
*WHILE  &amp;quot;solange ''Bedingung'' erfüllt ist, mache was&amp;quot;&lt;br /&gt;
[[Bild:While.png]]&lt;br /&gt;
&lt;br /&gt;
*DO...LOOP WHILE  &amp;quot;mache was, solange ''Bedingung'' erfüllt ist&amp;quot;&lt;br /&gt;
[[Bild:DoWhile.png]]&lt;br /&gt;
&lt;br /&gt;
Der Unterschied ist wichtig: Bei &amp;quot;WHILE&amp;quot; wird nur was gemacht, wenn die Bedingung schon zutrifft, Bei &amp;quot;DO..WHILE&amp;quot; werden die Befehle auf jeden Fall wenigstens einmal ausgeführt, erst dann wird gecheckt, ob wiederholt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Praxis===&lt;br /&gt;
Theoretisch sieht das ja gut aus, und mit Hochsprachen kann man das auch meist so formulieren. Beim Assembler geht das aber nur so schön übersichtlich, wenn man nur eine einzelne Bedingung hat. Eine einfache Zähl-Schleife in der &amp;quot;WHILE&amp;quot; Version:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$asm&lt;br /&gt;
   LDI   r25, 0         ' R25 = 0&lt;br /&gt;
   LDI   r24, 1         ' R24 = 1&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12           ' Der Befehl ist neu: vergleiche R25 mit dem festen Wert &amp;quot;12&amp;quot; &lt;br /&gt;
   BREQ  SchleifenAusgang  ' Wenn R25 = 12, verlassen wir die Schleife&lt;br /&gt;
   ADD   R25, R24          ' auf R25 den Wert von R24 draufaddieren &lt;br /&gt;
   RJMP  SchleifenBeginn   ' und wieder rauf zur Prüfung&lt;br /&gt;
SchleifenAusgang:&lt;br /&gt;
   ...&lt;br /&gt;
$end Asm&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Was geschieht, ist klar: R25 beginnt mit Null. Wenn der R25 NICHT= &amp;quot;12&amp;quot;, addieren wir &amp;quot;1&amp;quot; auf R25 und wiederholen das Ganze. Wenn R25 = &amp;quot;12&amp;quot;, verlassen wir die Schleife. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nehmen wir aber an, wir hätten zwei Bedingungen (es geht hier nicht um Sinn oder Unsinn der Abfrage):&lt;br /&gt;
*WHILE R25 NICHT= &amp;quot;12  UND R24 = &amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12           ' Der Befehl ist neu: vergleiche R25 mit dem festen Wert &amp;quot;12&amp;quot; &lt;br /&gt;
   BREQ  SchleifenAusgang  ' Wenn R25 = 12, verlassen wir die Schleife&lt;br /&gt;
   CPI   R24, 1            ' s.o&lt;br /&gt;
   BRNE  SchleifenAusgang  ' Wenn R24 NICHT= 1, verlassen wir die Schleife&lt;br /&gt;
   ADD   R25, R24          ' auf R25 den Wert von R24 draufaddieren &lt;br /&gt;
   RJMP  SchleifenBeginn   ' und wieder rauf zur Prüfung&lt;br /&gt;
SchleifenAusgang:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*WHILE R25 NICHT= &amp;quot;12  ODER R24 &amp;lt; R25&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 &lt;br /&gt;
SchleifenBody:&lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  SchleifenBody&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Wenn wir da nicht im Kommentar dazuschreiben, worum es geht, kennt sich ein Fremder erst nach einiger Überlegung aus.&lt;br /&gt;
&lt;br /&gt;
===Tips===&lt;br /&gt;
Mehrere Bedingungen in eine UND-ODER Beziehung sind immer fehleranfällig und leicht unübersichtlich&lt;br /&gt;
&lt;br /&gt;
*Als Erstes immer die RICHTIGE (und am besten verständliche) Lösung suchen, und erst dann durch Umformungen die &amp;quot;SCHÖNE&amp;quot; Lösung. &lt;br /&gt;
&lt;br /&gt;
*Also nochmal das obige &amp;quot;ODER&amp;quot; Beispiel, erst in der vollen Grundform&lt;br /&gt;
&lt;br /&gt;
 WHILE  ( R25 NICHT= &amp;quot;12 ) ODER  ( R24 &amp;lt; R25 )&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12           ' R25 &amp;lt;=&amp;gt; 12&lt;br /&gt;
   BREQ  R25_ist_12&lt;br /&gt;
   JMP   R25_ist_nicht_12&lt;br /&gt;
&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   ADD   R25, R24          ' der &amp;quot;BODY&amp;quot; steht ja fest &lt;br /&gt;
   RJMP  SchleifenBeginn   ' das ist auch sicher&lt;br /&gt;
&lt;br /&gt;
SchleifenAusgang:          ' ausgang gibt es (eigentlich) immer&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das fehlt was ? Ja, denn jetzt erst sollten wir die Ziele auch hinschreiben&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1. Wir machen den &amp;quot;body&amp;quot; immer, wenn   r25 nicht gleich 12&lt;br /&gt;
&lt;br /&gt;
also schreiben wir das hin&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12           ' &lt;br /&gt;
   BREQ  R25_ist_12&lt;br /&gt;
   JMP   R25_ist_nicht_12  ' abgehakt&lt;br /&gt;
&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25&lt;br /&gt;
&lt;br /&gt;
R25_ist_nicht_12:          ' &lt;br /&gt;
   ADD   R25, R24          ' &lt;br /&gt;
   RJMP  SchleifenBeginn   ' &lt;br /&gt;
&lt;br /&gt;
SchleifenAusgang:          ' &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Wir machen den &amp;quot;body&amp;quot; immer, wenn   r24 kleiner als r25&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12&lt;br /&gt;
   JMP   R25_ist_nicht_12           ' abgehakt&lt;br /&gt;
&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        ' abgehakt&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25  ' &lt;br /&gt;
&lt;br /&gt;
r24_ist_kleiner_r25:&lt;br /&gt;
R25_ist_nicht_12:           &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn    &lt;br /&gt;
&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Anmerkung: wir können an der selben Stelle beliebig viele Label vergeben&lt;br /&gt;
&lt;br /&gt;
3. Was ist, wenn r25 = 12 ?  dann müssen wir die zweite Bedingung prüfen (ist ja ein ODER) &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 ' abgehakt&lt;br /&gt;
   JMP   R25_ist_nicht_12           ' abgehakt&lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        ' abgehakt&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25  ' &lt;br /&gt;
&lt;br /&gt;
r24_ist_kleiner_r25:&lt;br /&gt;
R25_ist_nicht_12:           &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn    &lt;br /&gt;
&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
4. Bleibt nurmehr &amp;quot;r24 ist nicht kleiner r25&amp;quot;.  Da geht's offenbar dann hin, wenn KEINE der Bedingungen erfüllt ist, also: raus aus der Schleife&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 ' abgehakt &lt;br /&gt;
   JMP   R25_ist_nicht_12           ' abgehakt &lt;br /&gt;
&lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        ' abgehakt&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25  ' abgehakt&lt;br /&gt;
&lt;br /&gt;
r24_ist_kleiner_r25:&lt;br /&gt;
R25_ist_nicht_12:           &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
  &lt;br /&gt;
r24_ist_nicht_kleiner_r25:&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt ist das Ganze zwar nicht elegant, aber richtig und leicht nachvollziehbar.&lt;br /&gt;
&lt;br /&gt;
Wenn der Sprungbefehl und das Ziel unmittelbar hintereinander stehen, können wir uns den Sprung sparen. Also bauen wir etwas um, damit das auch so ist:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 '&lt;br /&gt;
   JMP   R25_ist_nicht_12           ' steht jetzt direkt dahinter&lt;br /&gt;
&lt;br /&gt;
r24_ist_kleiner_r25:                'den ganzen Block raufgeschoben &lt;br /&gt;
R25_ist_nicht_12:           &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
&lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        '&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25  ' steht jetzt direkt dahinter&lt;br /&gt;
 &lt;br /&gt;
r24_ist_nicht_kleiner_r25:&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und kürzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 &lt;br /&gt;
r24_ist_kleiner_r25:      &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        &lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ob das nun ein &amp;quot;WHILE&amp;quot; oder ein &amp;quot;DO..WHILE&amp;quot;  wird, hängt nurmehr davon ab, wo wir zu Beginn in die Befehlsfolge reinspringen. &lt;br /&gt;
*Von oben weg, wie es dort steht, ist es eine &amp;quot;WHILE&amp;quot; Schleife&lt;br /&gt;
*Eine &amp;quot;DO..WHILE&amp;quot; Schleife (Bedingung am Ende prüfen) wird es, wenn wir zuerst mit dem &amp;quot;Body&amp;quot; beginnen. Also &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   JMP   r24_ist_kleiner_r25     ' Erst die Aktion, DANN die Bedingung prüfen &lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 &lt;br /&gt;
r24_ist_kleiner_r25:      &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        &lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist doch praktisch ? &lt;br /&gt;
&lt;br /&gt;
Assembler-mäßig ist das nun ok und erträglich. Aber mit dem theoretischen WHILE-Flußdiagramm hat das nun nicht mehr viel gemeinsam.&lt;br /&gt;
&lt;br /&gt;
==Weg vom Simulator auf den µC==&lt;br /&gt;
Jetzt wird's Zeit, die ersten Schritte in die AVR-Realität zu machen, immer Simulator ist ja langweilig, fast so, als würden wir im Trockenen schwimmen lernen müssen. &lt;br /&gt;
&lt;br /&gt;
Bis jetzt haben wir vom Bascom ja nur die äussere Programmhülle verwendet, jetzt soll er doch auch wirklich was tun. &lt;br /&gt;
&lt;br /&gt;
===Datenaustausch mit BasCom===&lt;br /&gt;
Das funktioniert über Datenfelder, die wir irgendwie definieren müssen. Weil's so einfach ist, lassen wir das erstmal Bascom übernehmen. Das ist kein Rückschritt auf dem Weg zum Assemblerprogrammierer, denn das Definieren von Daten ist ja nichts anderes, als Felderen im SRAM (also im eigentlichen Arbeitsspeicher) Namen zuzuweisen. Über den Namen kann man diese Felder dann auch im Assembler ansprechen. &lt;br /&gt;
&lt;br /&gt;
*'''Bascom--&amp;gt;Assembler'''&lt;br /&gt;
Damit Bascom ein Feld für uns definiert, sagen wir einfach (aber außerhalb der &amp;quot;$asm&amp;quot; / &amp;quot;$end asm&amp;quot; Bereiches) &lt;br /&gt;
 DIM Meinfeld AS BYTE &lt;br /&gt;
Im Bascom, das weiß der Leser vielleicht ja schon, kann man da Werte reinschreiben, so wie wir das mit dem &amp;quot;LDI&amp;quot; Befehl bei Registern gemacht haben&lt;br /&gt;
  Meinfeld = 85 &lt;br /&gt;
Jetzt steht an irgendeiner Speicherstelle (egal wo, wir sprechen es eh' nur über den Namen an) der Binärwert von 85  (Hexadezimal &amp;amp;H55 oder in Bits &amp;amp;B01010101).&lt;br /&gt;
Dieses Feld können wir aber nun auch mit dem Assembler lesen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte           ' definition&lt;br /&gt;
&lt;br /&gt;
   Meinfeld = 85               'Wertzuweisung&lt;br /&gt;
&lt;br /&gt;
   $asm&lt;br /&gt;
   lds   r24, {Meinfeld}        '(das sind zwei geschwungene Klammern)&lt;br /&gt;
                                ' das ist wieder ein neuer Befehl: &amp;quot;LDS&amp;quot;&lt;br /&gt;
   $end Asm&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;LDS&amp;quot; lädt in ein beliebiges Register den Wert von der Speicherstelle, die &amp;quot;Meinfeld&amp;quot; heißt. &lt;br /&gt;
&lt;br /&gt;
*'''Assembler--&amp;gt;Bascom'''&lt;br /&gt;
Das geht auch umgekehrt. Wenn wir den Bascomteil noch etwas vervollständigen&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000               ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                     ' die Baudrate für das Terminal&lt;br /&gt;
                                 ' dadurch macht Bascom alle Einstellungen, die wir für das &lt;br /&gt;
                                 ' Terminal brauchen&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte      ' definition&lt;br /&gt;
&lt;br /&gt;
   $asm&lt;br /&gt;
   LDI   R24, 85&lt;br /&gt;
   STS   {Meinfeld}, R24  ' &amp;quot;STS&amp;quot; ist das Gegenstück zu &amp;quot;LDS&amp;quot;, also vom Register zum Speicher&lt;br /&gt;
   $end Asm&lt;br /&gt;
&lt;br /&gt;
   PRINT  str(Meinfeld)   ' Und schon gibt uns Bascom den Wert (dezimal) auf dem Terminal aus&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Das ist der Vorteil den wir haben, wenn wir Assembler mit Bascom anfangen und nicht mit einem &amp;quot;richtigen&amp;quot; Assembler wie das AVR-Studio. Denn das Gefummel mit der Terminalausgabe erledigt alles Bascom, sonst müßten wir es selbst erst wo abschreiben oder lernen, bis wir auch nur einen Pieps auf dem Terminal sehen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Wenn es interessiert: Das, was wir zuletzt im Assembler geschrieben haben, ist auch genau das, was Bascom an Maschinencode produziert, wenn wir &lt;br /&gt;
 Meinfeld = 85&lt;br /&gt;
hinschreiben&lt;br /&gt;
&lt;br /&gt;
*'''Bascom--&amp;gt;Assembler--&amp;gt;Bascom'''&lt;br /&gt;
Noch immer können wir nur mit fest einprogrammierten Werten arbeiten, d.h. für was anderes als &amp;quot;85&amp;quot; müssen wir ändern, übersetzen und brennen.&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun versuchen, etwas über das Terminal einzugeben, bearbeiten und dann wieder ausgeben. &lt;br /&gt;
&lt;br /&gt;
'''Flußdiagramm'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:InOut.png]]&lt;br /&gt;
&lt;br /&gt;
Den Input lassen wir Bascom übernehmen. INKEY() holt einen Wert von der Tastatur. Er wartet aber nicht, bis was gedrückt wird, sondern gibt einfach NULL aus, wenn nix da ist. &lt;br /&gt;
Wir arbeiten also nur etwas, wenn ein Wert &amp;gt; NULL da ist. &lt;br /&gt;
&lt;br /&gt;
*Zuerst übernehmen wir nur den Part &amp;quot;Bearbeitung&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000               ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                     ' die Baudrate für das Terminal&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte             &lt;br /&gt;
DO&lt;br /&gt;
   Meinfeld = INKEY()            '&amp;quot;EINGABE&amp;quot;&lt;br /&gt;
   IF Meinfeld &amp;lt;&amp;gt; 0 THEN         '&amp;quot;BEDINGUNG&amp;quot;&lt;br /&gt;
       $asm                      '&amp;quot;BEARBEITUNG&amp;quot;&lt;br /&gt;
       lds   r24, {Meinfeld}&lt;br /&gt;
       '-------- Da kommt dann Code rein&lt;br /&gt;
       STS   {Meinfeld}, R24  &lt;br /&gt;
       $end Asm&lt;br /&gt;
       PRINT  str(Meinfeld)      '&amp;quot;AUSGABE&amp;quot;&lt;br /&gt;
   END IF&lt;br /&gt;
&lt;br /&gt;
   LOOP                          'Wiederholung&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, wie das Beispiel jetzt ist, ändern wir aber nichts wirklich, sondern schauen mal, was passiert.&lt;br /&gt;
&lt;br /&gt;
Und es passiert Merkwürdiges:&lt;br /&gt;
&lt;br /&gt;
Wenn wir das laufen lassen und in vielleicht gewohnter Manier &amp;quot;85&amp;quot; tippen und &amp;lt;ENTER&amp;gt; drücken, erscheint auf dem Terminal &lt;br /&gt;
 56&lt;br /&gt;
 53&lt;br /&gt;
 13&lt;br /&gt;
Einerseits ist es ja klar, wenn wir drei Tasten drücken, kriegen wir auch dreimal was zurück, aber wie bekommen wir dann in EIN Byte EINE Zahl &amp;quot;85&amp;quot; ? &lt;br /&gt;
&lt;br /&gt;
Also ein kurzer Side-Step.&lt;br /&gt;
&lt;br /&gt;
==Das Byte und seine vielen Bedeutungen==&lt;br /&gt;
&lt;br /&gt;
Nach wie vor ist klar: Ein Byte besteht aus 8 Bit, die in 256 Möglichkeiten kombiniert werden können. &lt;br /&gt;
&lt;br /&gt;
Das sagt aber nicht aus, was irgendeine dieser Bit-Kombinationen eigentlich '''bedeutet'''.&lt;br /&gt;
&lt;br /&gt;
*Das Byte als Bit-Container&lt;br /&gt;
Da hat jedes Bit seine eigene Bedeutung, unabhängig von den anderen. Also einfach &amp;quot;Schalter&amp;quot; und/oder JA-Nein Anzeigen. &lt;br /&gt;
&lt;br /&gt;
Typisch: '''Status-Register (SREG)'''.&lt;br /&gt;
&lt;br /&gt;
*Das Byte als (binär) Zahl. Hier repräsentieren die 256 Möglichkeiten wirklich die Zahlen von 0-255. Mit diesen Zahlen kann nach den binären Regeln gerechnet werden. &lt;br /&gt;
*Das Byte als zwei '''BCD'''Zahlen. Da besteht das Byte eigentlich aus zweimal 4 Bit (&amp;quot;Nibbles&amp;quot;). Jedes Nibble stellt die Zahlen 0 - 9 dar. Ist inzwischen eine recht exotische Verwendung, überhaupt als &amp;quot;gepacktes&amp;quot; Format, wo in einem Nibble auch noch ein Vorzeichen reinkodiert wurde. &lt;br /&gt;
*Das Byte als (binär) Zahl mit Vorzeichen. Das ist eine Kombination von Zahl und Schalter. Das Bit 2^^7 zeigt das Vorzeichen an. Solange es NULL ist, können die restlichen 7 Bit für Zahlen von (+) 0 - 127 normal verwendet werden. Ist das Vorzeichenbit = &amp;quot;1&amp;quot;, gelten die anderen Bit als Negativ-Zahl. Und sie werden als 2- Komplement dargestellt, d.h. 11111111 = -1. Dadurch geht das Bereich von -128 (10000000) bis +127 (01111111).&lt;br /&gt;
*Das Byte als Zahl, die Zahl ist aber ein Tabellenwert in einer standardisierten Tabelle.  &lt;br /&gt;
Typisch: '''ASCII und seine Varianten'''. Was heißt das ? Nun, gespeichert und hin-und hergeschickt wird zum Beispiel 01000001, das bedeutet aber nicht &amp;amp;H41 oder dezimal 65, sondern das, was in der ASCII-Tabelle an der Stelle 65 steht, und das ist ein großes &amp;quot;A&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Ich werd' mich hüten, hier eine ASCII-Tablle reinzustellen, die gibt es im Internet in Massen. Festgelegt sind die Werte 0 - 127. Wichtig ist nur die Gruppierung:&lt;br /&gt;
*0-31 sind &amp;quot;Steuerzeichen&amp;quot; für die Kommunikation. (&amp;quot;13&amp;quot; stellt das berühmte &amp;lt;ENTER&amp;gt; dar)&lt;br /&gt;
*32 bezeichnet eine Leerstelle (&amp;quot;blank&amp;quot;)&lt;br /&gt;
*48 - 57  für die Ziffern 0 - 9&lt;br /&gt;
*65 - 90  Großbuchstaben &amp;quot;A&amp;quot; bis &amp;quot;Z&amp;quot;&lt;br /&gt;
*97 - 122 Kleinbuchstaben &amp;quot;a&amp;quot; bis &amp;quot;z&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Numerischer Input vom Terminal==&lt;br /&gt;
Nachdem sich offenbar unterscheidet, was wir direkt vom Terminal bekommen und was wir davon brauchen können, brauchen wir ein zweites Datenfeld. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000               ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                     ' die Baudrate für das Terminal&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte             ' das bekommen wir vom Terminal&lt;br /&gt;
Dim Meinezahl As Byte            ' das soll die eigentliche Zahl werden&lt;br /&gt;
&lt;br /&gt;
DO&lt;br /&gt;
   Meinfeld = INKEY()            '&amp;quot;EINGABE&amp;quot;&lt;br /&gt;
   IF Meinfeld &amp;lt;&amp;gt; 0 THEN         '&amp;quot;BEDINGUNG&amp;quot;&lt;br /&gt;
       $asm                      '&amp;quot;BEARBEITUNG&amp;quot;&lt;br /&gt;
       '....   CODE .... (s.u.)&lt;br /&gt;
       $end Asm&lt;br /&gt;
       PRINT  str(Meinezahl)     'die zeigt er nach jeder Taste an&lt;br /&gt;
   END IF&lt;br /&gt;
   LOOP                          'Wiederholung&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Soweit das Gerüst bzw. der Rahmen. In &amp;quot;Meinfeld&amp;quot; stellt der Bascom rein, was getippt wurde und wir müssen das verarbeiten und stellen das jeweilige Ergebnis nach &amp;quot;Meinezahl&amp;quot; rein. &lt;br /&gt;
&lt;br /&gt;
Was ist zu tun ? &lt;br /&gt;
*Prüfen, was getippt wurde.&lt;br /&gt;
*davon abhängig &lt;br /&gt;
**Meinfeld=13 (&amp;lt;ENTER&amp;gt;). Die bisher bekommene Zahl ist komplett, jetzt könnten wir mit ihr irgendetwas rechnen oder sonstwas tun. &lt;br /&gt;
**Meinfeld= 48-57 ('0'-'9'). &lt;br /&gt;
***Das, was bisher in &amp;quot;Meinezahl&amp;quot; steht, multiplizieren wir mit 10, an der Einerstelle steht nun auf jeden Fall einen NULL, die bisherigen Zahlen links davon.&lt;br /&gt;
***Und dann müssen wir das, was reingekommen ist, auf binär 0-9 umwandeln und draufaddieren. &lt;br /&gt;
**Alles Andere ignorieren wir einfach.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000                                          ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                                                ' die Baudrate für das Terminal&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte&lt;br /&gt;
Dim Meinezahl As Byte&lt;br /&gt;
Do&lt;br /&gt;
   Meinfeld = Inkey()                                       '&amp;quot;EINGABE&amp;quot;&lt;br /&gt;
   If Meinfeld &amp;lt;&amp;gt; 0 Then                                    '&amp;quot;BEDINGUNG&amp;quot;&lt;br /&gt;
       $asm                                                 '&amp;quot;BEARBEITUNG&amp;quot;&lt;br /&gt;
       lds     r22, {Meinezahl}                             'die bisherige Zahl&lt;br /&gt;
       lds     r24, {Meinfeld}                              'die neue Ziffer&lt;br /&gt;
       cpi     r24, 13                                      '&amp;lt;ENTER&amp;gt; ?&lt;br /&gt;
       brne    Is_numerisch&lt;br /&gt;
       ' ENTER Gedrückt&lt;br /&gt;
       ' Zahl verarbeiten&lt;br /&gt;
       ' dann löschen für ein Neues&lt;br /&gt;
       clr   r22&lt;br /&gt;
       rjmp    Fertig&lt;br /&gt;
Is_numerisch:&lt;br /&gt;
       cpi     r24, 48                                      '48 = '0'&lt;br /&gt;
       brlo    Fertig                                       'keine Ziffer--&amp;gt;ignorieren&lt;br /&gt;
       cpi     r24, 57 + 1                                  '57 = '9'&lt;br /&gt;
       brsh    Fertig                                       'keine Ziffer--&amp;gt;ignorieren&lt;br /&gt;
Einfügen:&lt;br /&gt;
' Bisherige Zahl * 10   Methode:  n * 10 =&amp;gt; n (8 + 2) =&amp;gt; n * 8 + n * 2&lt;br /&gt;
       lsl     r22                                          ' (n * 2)&lt;br /&gt;
       mov     r23, r22                                     ' kopieren&lt;br /&gt;
       lsl     r23                                          ' (n * 2) * 2&lt;br /&gt;
       lsl     r23                                          ' (n * 2 * 2) * 2&lt;br /&gt;
       add     r22, r23                                     'n * 8 + n * 2&lt;br /&gt;
' neue Ziffer umwandeln&lt;br /&gt;
       andi    r24, 15&lt;br /&gt;
' und addieren&lt;br /&gt;
       add     r22, r24&lt;br /&gt;
Fertig:&lt;br /&gt;
       STS   {Meinezahl}, R22&lt;br /&gt;
       $end Asm&lt;br /&gt;
       Print Str(meinezahl)                                 '&amp;quot;AUSGABE&amp;quot;&lt;br /&gt;
   End If&lt;br /&gt;
   Loop                                                     'Wiederholung&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Da sind ein paar Sachen dabei, die hatten wir noch nicht&lt;br /&gt;
&lt;br /&gt;
*Ich beginne mal von hinten: Bei &amp;quot;Fertig&amp;quot; wird das Register R22 nach &amp;quot;Meinezahl&amp;quot; geschrieben, Bascom zeigt es dann her. Wir müssen also sehen, das in R22 immer das Richtige drin steht.&lt;br /&gt;
*Also, jetzt wieder von vorn, wir holen den bisherigen Wert von &amp;quot;Meinezahl&amp;quot; gleich mal ins R22.&lt;br /&gt;
(Das ist am Anfang einfach NULL)&lt;br /&gt;
*Dann die neue Eingabe in R24&lt;br /&gt;
*Die vergleichen wir mir &amp;quot;13&amp;quot; == &amp;lt;ENTER&amp;gt;&lt;br /&gt;
**ist es eine andere Eingabe (BRNE), schauen wir bei &amp;quot;Is_numerisch&amp;quot; weiter&lt;br /&gt;
**Wenn aber ja, könnten wir jetzt was tun. Wir machen aber weiter nichts, löschen aber R22 und damit &amp;quot;Meinezahl&amp;quot; (s.o) auf NULL, damit wir für eine neue Eingabe bereit sind.&lt;br /&gt;
*Is_numerisch: &lt;br /&gt;
**Die Eingabe ist kleiner als 48 ('0'), keine Ziffer, wir sind auch schon fertig&lt;br /&gt;
**Jetzt müßten wir vergleichen, ob die Eingabe größer als 57 ('9') ist. Das geht nicht [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User#Verzweigen|(siehe weiter oben bei den Verzweigungen)]], also vergleichen wir mit 57+1, bei gleich oder größer ist die Eingabe auch keine Ziffer, also --&amp;gt; fertig.&lt;br /&gt;
*Einfügen: &lt;br /&gt;
**Bisheriger Wert * 10: Bei manchen µC gibt es einen Multiplikationsbefehl, aber diese einfache Rechnung machen wir zu Fuß, ist einfach mehr Spaß und mehr neue Befehle. &lt;br /&gt;
&lt;br /&gt;
n * 10 kann man ja zerlegen in n*(8+2). Und sowohl 8 als auch 2 sind ja 2-er Potenzen, da kann einfach Bit-weise nach links schieben.&lt;br /&gt;
 &amp;quot;LSL  R22&amp;quot;  ist gleich  R22 * 2   &amp;quot;LSL  Logical Shift Left&amp;quot;&lt;br /&gt;
damit haben wir schon mal Meinezahl * 2 gerechnet. Das kopieren wir nach R23 und schieben dort noch zweimal nach links. In R23 steht nun also insgesamt Meinezahl * 8. Das addieren wir nun.  &lt;br /&gt;
*Umwandeln: Die Ziffern '0'-'9', also 48 - 57 schauen hexadezimal ja so aus:&lt;br /&gt;
 0x30 = dezimal 48 = ASCII '0' bis &lt;br /&gt;
 0x39 = dezimal 57 = ASCII '9'&lt;br /&gt;
Wir brauchen also nur die '3' irgendwie zu löschen, dann haben wir sofort unsere 0-9. Das geschieht durch &lt;br /&gt;
 ANDI     R24, 15          ' &amp;quot;AND&amp;quot; mit einer Konstanten &lt;br /&gt;
In R24 bleiben nur die 1-er übrig, wo auch in '15' ein 1-er ist. Damit ist die '3' weg.&lt;br /&gt;
*Addieren&lt;br /&gt;
 ADD  R22, r24           'Meinezahl + neuerWert&lt;br /&gt;
und fertig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine Anmerkung: Wir haben noch keine Prüfung, ob die Zahl für ein Byte nicht zu groß wird. Wenn wir also 9999 eintippen, kommt Schrott heraus.&lt;br /&gt;
&lt;br /&gt;
==Mehr als ein Byte==&lt;br /&gt;
Mit einzelnen Bytes geht es ja jetzt schon recht gut. Aber es gibt ja auch größere Zahlen. &lt;br /&gt;
===16 Bit Zahlen===&lt;br /&gt;
Das nächstgrößere ist eine 16-Bit Zahl. Bei Bascom ist das dann ein &lt;br /&gt;
*WORD   für den Zahlenbereich 0 - 65535 und &lt;br /&gt;
*INTEGER für -32768 bis +32767&lt;br /&gt;
&lt;br /&gt;
Beides wird in zwei hintereinanderliegenden Bytes gespeichert, also  &lt;br /&gt;
 MeinByte und &lt;br /&gt;
 MeinByte + 1&lt;br /&gt;
Und zwar so, daß erst das niederwertigere und dann das höherwertige Byte gespeichert werden. &lt;br /&gt;
Also die 16-Bit Binärzahl  &lt;br /&gt;
 0001001000110100 = Hexadezimal &amp;amp;H1234 &lt;br /&gt;
steht im Speicher in zwei Bytes als&lt;br /&gt;
 00110100 00010010 =  &amp;amp;H34 &amp;amp;H12&lt;br /&gt;
&lt;br /&gt;
Genauso ist es mit den Registern, üblicherweise steht das erste Byte in einem geradzahligen Register und das zweite im nächsthöherem. z.B.:&lt;br /&gt;
 R24: 00110100 =  &amp;amp;H34&lt;br /&gt;
 R25: 00010010 =  &amp;amp;H12&lt;br /&gt;
Sowas nennt man dann &amp;quot;Register-Paar&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Der AVR µC kann einige seiner 32 Register als Registerpaar verwenden. Das sind &lt;br /&gt;
 R0:R1       (aber nur bei den Multiplikationsbefehlen für das Ergebnis)&lt;br /&gt;
 R24:R25     (für 16-Bit Berechnungen)&lt;br /&gt;
 R26:R27     (für 16-Bit Berechnungen und als &amp;quot;Pointer&amp;quot; X)&lt;br /&gt;
 R28:R29     (für 16-Bit Berechnungen und als &amp;quot;Pointer&amp;quot; Y)&lt;br /&gt;
 R30:R31     (für 16-Bit Berechnungen und als &amp;quot;Pointer&amp;quot; Z)&lt;br /&gt;
Wobei es aber nur zwei solche Berechnungen gibt&lt;br /&gt;
 ADIW     R24 , zahl       ' addieren zahl auf r24:r25&lt;br /&gt;
 SBIW     R24 , zahl       ' subtrahieren zahl von r24:r25&lt;br /&gt;
Die anderen 16-Bit Befehle sind eher Adress-Arithmetik. &lt;br /&gt;
&lt;br /&gt;
*Datenaustausch Bascom &amp;lt;=&amp;gt; Assembler&lt;br /&gt;
Um ein WORD (oder INTEGER) mit Bascom auszutauschen, geht z.B. folgendes&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 DIM word_1 AS WORD&lt;br /&gt;
     $asm&lt;br /&gt;
     LDS      R24, {word_1}    'holen Byte 1  (bits 0-7)&lt;br /&gt;
     LDS      R25, {word_1+1}  'holen Byte 2  (bits 8-15)&lt;br /&gt;
     ADIW     R24, 42          'addieren von 42 auf das Paar r24:r25&lt;br /&gt;
     STS      {word_1}, R24    'speichern Byte 1&lt;br /&gt;
     STS      {word_1+1}, R25  'speichern Byte 2&lt;br /&gt;
     $end asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Arithmetik mit 16 Bit &lt;br /&gt;
Der Unterschied zur 8-Bit Verarbeitung ist eigentlich nicht groß. Es gibt zu allen Arithmetik Befehlen des AVR eine &amp;quot;Carry&amp;quot;-Variante, die einen Übertrag von der Aktion vorher berücksichtigt. &lt;br /&gt;
&lt;br /&gt;
Angenommen zwei 16-Bit Werte in den Registerpaaren R24:R25 und R22:R23&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ADD    R24, R22           'Addieren der niederwertigen Bytes&lt;br /&gt;
 ADC    R25, R23           'Addieren der höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
&lt;br /&gt;
 SUB    R24, R22           'Subtrahieren der niederwertigen Bytes&lt;br /&gt;
 SBC    R25, R23           'Subtrahieren der höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Vergleichen mit 16 Bit &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 CP     R24, R22           'Vergleich der niederwertigen Bytes&lt;br /&gt;
 CPC    R25, R23           'Vergleich der höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
           ' jetzt können die normalen &amp;quot;bedingte Verzweigung&amp;quot;-Befehle verwendet werden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===32 Bit Zahlen===&lt;br /&gt;
Bei Bascom heissen die LONG und die sind immer Vorzeichenbehaftet. Im AVR Instructionset gibt es keine 32-Bit Befehle. Die physische Speicherung ist wie bei den 16-Bit Feldern&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 DIM long_1 AS LONG&lt;br /&gt;
     $asm&lt;br /&gt;
     LDS      R24, {long_1}    'holen Byte 1  (bits 0-7)&lt;br /&gt;
     LDS      R25, {long_1+1}  'holen Byte 2  (bits 8-15)&lt;br /&gt;
     LDS      R26, {long_1+2}  'holen Byte 3  (bits 16-23)&lt;br /&gt;
     LDS      R27, {long_1+3}  'holen Byte 4  (bits 24-31)&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Arithmetik mit 32 Bit &lt;br /&gt;
Eigentlich läuft das ab 16-Bit immer gleich ab, und man kann genauso 24-Bit wie 40 oder 48 Bit rechnen. &lt;br /&gt;
Angenommen zwei 32-Bit Werte in den Registern  R16-&amp;gt;R19 und R20-&amp;gt;R23&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ADD    R16, R20           'Addieren der niederwertigen Bytes&lt;br /&gt;
 ADC    R17, R21           'Addieren des nächst-höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
 ADC    R18, R22           'Addieren des nächst-höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
 ADC    R19, R23           'Addieren des nächst-höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Single und Double Zahlen===&lt;br /&gt;
das sind 32- bzw. 64-Bitzahlen, allerdings haben die eine eigene Float-Struktur. Das ist dann schon ein eigenes Thema, mit denen was zu machen. &lt;br /&gt;
&lt;br /&gt;
===Strings===&lt;br /&gt;
Strings sind Bytefolgen, in denen ASCII-Zeichen einfach von links nach rechts gespeichert werden können. Das Ende-Kennzeichen der immer variabel langen Texte ist ein NULL-Byte. Daher braucht in Bascom ein String für (max) 20 Zeichen lange Strings auch in Wirklichkeit 21 Byte Speicher.&lt;br /&gt;
&lt;br /&gt;
Strings sind gut geeignet, eine andere Art vorzustellen, wie man Daten lesen oder speichern kann: Über POINTER-Register&lt;br /&gt;
&lt;br /&gt;
Gleich ganz in die Praxis: Wir wollen die Länge eines Bascom-Strings festellen, natürlich im Assembler&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000                                          ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                                                ' die Baudrate für das Terminal&lt;br /&gt;
&lt;br /&gt;
Dim Text As String * 20                                     ' das ist der String&lt;br /&gt;
Dim Strlen As Byte                                          ' da sol die Länge rein&lt;br /&gt;
&lt;br /&gt;
      Text = &amp;quot;Hello, world !&amp;quot;                               ' wir befüllen den String&lt;br /&gt;
&lt;br /&gt;
      $asm&lt;br /&gt;
      Loadadr Text , X            '&amp;quot;adresse von Text nach R26:r27&amp;quot; &lt;br /&gt;
      clr      r24                'löschen vom Zähler&lt;br /&gt;
Schleife: &lt;br /&gt;
      ld       r22, X+            ' s.u.&lt;br /&gt;
      cpi      r22, 0             'Vergleichen mit NULL&lt;br /&gt;
      breq     Fertig             'fertig&lt;br /&gt;
      inc      r24                'Zähler erhöhen&lt;br /&gt;
      rjmp     Schleife           'weiter&lt;br /&gt;
Fertig:&lt;br /&gt;
      sts   {strlen}, r24         'ergebnis ablegen&lt;br /&gt;
      $end Asm&lt;br /&gt;
&lt;br /&gt;
      Print Str(strlen)           'herzeigen&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*'''Loadadr''' ist ein Bascom-Statement, mit dem das Registerpaar R26:R27 mit der '''Adresse''' von &amp;quot;Text&amp;quot; geladen wird. In einem &amp;quot;echten&amp;quot; Assembler kann man das auch assemblermäßiger formulieren, aber in Bascom muß man das so machen&lt;br /&gt;
*'''LD'''&lt;br /&gt;
      ld       r22, X+        'Das Zeichen, wo X hinzeigt, nach R22 und X um eins erhöhen&lt;br /&gt;
Den Rest kennen wir eigentlich schon.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''ST''' ist übrigens das Gegenstück zu LD&lt;br /&gt;
      st       X+, r22        'R22 dorhin, wo X hinzeigt, und dann X um eins erhöhen&lt;br /&gt;
&lt;br /&gt;
==Bascom als Messlatte==&lt;br /&gt;
Eine sehr gute Möglichkeite, Assembler zu trainieren, ist es, irgendwelche Bascom-Statements durch eigene Inline-Assembler-Blöcke zu ersetzen. Dabei kann man immer auch Varianten ausprobieren. &lt;br /&gt;
*Beispiel&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DIM  Wert1 AS WORD&lt;br /&gt;
DIM  Wert2 AS WORD&lt;br /&gt;
        Wert1 = Wert2   ' Bascom-Statement&lt;br /&gt;
&lt;br /&gt;
'als Inline-Assembler  EINE Möglichkeit&lt;br /&gt;
        $asm&lt;br /&gt;
        LDS     R24, {Wert1}&lt;br /&gt;
        STS     {Wert2}, R24&lt;br /&gt;
        LDS     R24, {Wert1+1}&lt;br /&gt;
        STS     {Wert2+1}, R24&lt;br /&gt;
        $end asm&lt;br /&gt;
'als Inline-Assembler  ANDERE Möglichkeit&lt;br /&gt;
        $asm&lt;br /&gt;
        LOADADR Wert1, X&lt;br /&gt;
        LD     R24, X+&lt;br /&gt;
        LD     R25, X+&lt;br /&gt;
        LOADADR Wert2, X&lt;br /&gt;
        ST     X+, R24&lt;br /&gt;
        ST     X+, R25&lt;br /&gt;
        $end asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bascom_Inside-Code|Es gibt hier ein paar Beispiele]], wie Bascom dieses oder jenes in Maschinencode umsetzt, dabei ist es auch sehr lehrreich, erstmal zu überlegen, wie man es selbst machen würde, und dann erst nachzusehen, wie es Bacom gelöst hat.&lt;br /&gt;
&lt;br /&gt;
==ALU und Status-Register (SREG)==&lt;br /&gt;
Bisher haben wir nur von Zero-Bits und &amp;quot;Oberflow&amp;quot; bzw. Carry-Bit gesprochen und uns da ein wenig drübergeschwindelt. Es ist ja auch tatsächlich so, daß man mit dem bisher erworbenen Wissen ganze Legionen von Robotern programmieren kann. &lt;br /&gt;
&lt;br /&gt;
Aber ganz kommt man an den diversen Flags im Statusregister ja doch nicht vorbei, schon garnicht, wenn man ernsthafte Berechnungen anstellen will.  &lt;br /&gt;
&lt;br /&gt;
Zwei Bit im SREG brauchen wir an dieser Stelle aber nicht weiter zu beachten&lt;br /&gt;
*T-Bit oder Transfer-Bit. Das dient für einige Befehle als &amp;quot;Zwischendepot&amp;quot; für einzelne Bits&lt;br /&gt;
*Global Interrupt Enable-Bit. Wie der Name sagt, werden damit einfach alle Interrupts zugelassen oder eben nicht. Von Interrupts haben wir aber noch garnicht gesprochen &lt;br /&gt;
*Die andern Bits dienen der ALU (Arithmetic-Logical-Unit) als Ergebnis-Anzeige bei den ganzen arithmetisch-logischen Befehlen&lt;br /&gt;
===ALU (Recheneinheit)=== &lt;br /&gt;
Immer, wenn zwei Bytes zu vermangeln sind, geht das mit der Recheneinheit. Die hat 4 Byte zur Verfügung:&lt;br /&gt;
*Input-Byte A&lt;br /&gt;
*Input-Byte B&lt;br /&gt;
*Output-Byte R&lt;br /&gt;
*Statusregister SREG&lt;br /&gt;
(Daß er dabei Input A gleichzeitig als Output verwendet, ignorieren wir mal einfach, das verwirrt nur und ändert nix grundsätzliches)&lt;br /&gt;
&lt;br /&gt;
====Ein-Bit-Volladdierer==== &lt;br /&gt;
Wir wissen ja alle noch, wie das mit dem Addieren im 2-er System funktioniert ?  &lt;br /&gt;
 A   B    R&lt;br /&gt;
 0 + 0  = 0&lt;br /&gt;
 0 + 1  = 1&lt;br /&gt;
 1 + 0  = 1&lt;br /&gt;
 1 + 1  = 0  und Überlauf&lt;br /&gt;
Diesen Überlauf muß man nun an das nächsthöhere Bit weitergeben. Andererseits kommt von dem niedrigeren Bit ja auch ein Überlauf daher. Also sieht das für ein Bit dann so aus&lt;br /&gt;
[[Bild:ALU1.png]]&lt;br /&gt;
&lt;br /&gt;
====Mit 8-Bit==== &lt;br /&gt;
Für alle 8 Bit wird es etwas größer, ein paar direkte Status-Bit hab ich gleich eingezeichnet&lt;br /&gt;
[[Bild:ALU2.png]]&lt;br /&gt;
*Der Schalter &amp;quot;mit od. ohne Carry&amp;quot; ist da, ob man einen vorherigen Überlauf miteinbeziehen will &lt;br /&gt;
 ADD   register, register         OHNE Carry &lt;br /&gt;
 ADC   register, register         MIT  Carry&lt;br /&gt;
*N-Bit:  Ist einfach der Wert (0 oder 1), den das Bit 2^^7 im Ergebnis hat&lt;br /&gt;
*Z-Bit:  Steht im Ergebnis alles auf 0, wird das gesetzt&lt;br /&gt;
*Half-Carry: Das wird gesetzt, wenn es vom Bit 2^^3 einen Überlauf in Richtung 2^^4 gegeben hat. (Das braucht man, wenn man mit BCD-Zahlen, also nur von 0-9 rechnen will. Dazu später, wenn Interesse da ist)&lt;br /&gt;
*Carry-Bit: Ist nun klar, das ist einfach der Überlauf vom 2^^7 Bit.&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;S&amp;quot; und &amp;quot;V&amp;quot; Bit====&lt;br /&gt;
=====8 Bit mit Vorzeichen=====&lt;br /&gt;
Kurze Wiederholung *gähn*, wie das mit den Vorzeichen ist: Man &amp;quot;spiegelt&amp;quot; die Zahlen an der Null-Stelle. Nehmen wir an, wir haben grad eine NULL im Byte. Ziehen wir jetzt 1 ab, soll ja offenbar -1 rauskommen. Was steht im Byte tatsächlich drin ? Richtig, Hexadezimal FF. Und genauso schaut auch -1 eben aus. Alle diese negativen Zahlen haben gemeinsam, daß zumindest das Bit 2^^7 eine 1 zeigt. Daran erkennt man, daß die Zahl negativ ist. Das funktioniert aber nur so bis -128 (hex 80) und +127 (hex 7F). &lt;br /&gt;
&lt;br /&gt;
Negative Zahlen werden also dargestellt als &amp;quot;2-er Komplement&amp;quot;. &lt;br /&gt;
*Man dreht 0-er und 1-er im Byte um&lt;br /&gt;
*und addiert &amp;quot;1&amp;quot; drauf. &lt;br /&gt;
           0b01111000 hex 78 = dezimal 120&lt;br /&gt;
 umdrehen: 0b10000111 hex 87  &lt;br /&gt;
 +1      : 0b10001000 hex 88  Das stellt jetzt -120 dar.&lt;br /&gt;
&lt;br /&gt;
=====Rechnen mit vorzeichenbehafteten 8 Bit=====&lt;br /&gt;
Der Sinn und Zweck des &amp;quot;S&amp;quot; und &amp;quot;V&amp;quot; Bit ist nun leicht verständlich. &lt;br /&gt;
*Bisher wurde ja addiert&lt;br /&gt;
     0b01111000 hex 78 = dezimal 120&lt;br /&gt;
   + 0b00001010 hex 0A = dezimal  10&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82 = dezimal 130       &lt;br /&gt;
*Addiert wird jetzt immer noch genauso, aber jetzt hat das Bit 2^^7 eine andere Bedeutung, weil es das Vorzeichen darstellt&lt;br /&gt;
*Ein Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0b10000000 hex 80 = dezimal -128&lt;br /&gt;
   + 0b00000010 hex 02 = dezimal   +2&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82  (Vorzeichenbit ist gesetzt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vorzeichenbit gesetzt-&amp;gt; hex 82 ist also ein zweier-Komplement, und damit ist es -126.&lt;br /&gt;
&lt;br /&gt;
*Aber nun:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0b01111000 hex 78 = dezimal +120&lt;br /&gt;
   + 0b00001010 hex 0A = dezimal  +10&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82  (Vorzeichenbit ist gesetzt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vorzeichenbit gesetzt-&amp;gt; hex 82 ist also ein zweier-Komplement, und damit ist es -126.&lt;br /&gt;
&lt;br /&gt;
'''Bitte ???'''&lt;br /&gt;
&lt;br /&gt;
Das kann ja wohl nicht sein, es muß ja +130 rauskommen. &lt;br /&gt;
&lt;br /&gt;
Das Carry-Bit hilft nichts, das ist immer NULL geblieben. &lt;br /&gt;
&lt;br /&gt;
*Genau da hilft das &amp;quot;V&amp;quot; Bit&lt;br /&gt;
&lt;br /&gt;
[[Bild:ALU3.png]]&lt;br /&gt;
&lt;br /&gt;
Das V-Bit = 1, &lt;br /&gt;
*wenn entweder die beiden Input-Bytes positiv waren (2^^7=0) UND das Ergebnis aber 2^^7=1 zeigt. &lt;br /&gt;
*wenn beiden Input-Bytes negativ waren (2^^7=1) UND das Ergebnis aber 2^^7=0 zeigt. &lt;br /&gt;
&lt;br /&gt;
Im Prinzip sagt also das V-Bit aus, ob das Vorzeichenbit im Ergebnis durch einen Überlauf zustandegekommen ist. &lt;br /&gt;
&lt;br /&gt;
*Und das &amp;quot;S&amp;quot;-Bit ? Das kombiniert das Vorzeichen im Ergebnis mit diesem &amp;quot;V&amp;quot;-Bit.&lt;br /&gt;
**S=1, wenn das Vorzeichenbit im Ergebnis zwar NULL ist, aber nur, weil ein Überlauf stattgefunden hat. Das Ergebnis ist also trotzdem negativ und stellt ein 2-er Komplement dar.  &lt;br /&gt;
**S=1, wenn das Vorzeichenbit im Ergebnis EINS ist, und zwar OHNE, daß ein Überlauf stattgefunden hätte. Das Ergebnis ist also korrekt negativ und stellt auch ein 2-er Komplement dar&lt;br /&gt;
&lt;br /&gt;
Ob an das nächsthöhere Byte ein Überlauf weiterzugeben ist, sagt bei Vorzeichen-Bytes nun das &amp;quot;V&amp;quot; Bit.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nochmal die obigen Vorzeichen-Beispiele zum mitsingen, aber diesmal mit den N, V und S Flags &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0b10000000 hex 80 = dezimal -128&lt;br /&gt;
   + 0b00000010 hex 02 = dezimal   +2&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82   N=1 V=0 S=1    Ist also negativ, 2-er Kompl. kein Überlauf --&amp;gt;  -126&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     0b01111000 hex 78 = dezimal +120&lt;br /&gt;
   + 0b00001010 hex 0A = dezimal  +10&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82   N=1 V=1 S=0    Ist also positiv, 2^^7 löschen, der Rest bleibt &lt;br /&gt;
                                        (dezimal 2), hat aber Überlauf, der ist +128 wert, &lt;br /&gt;
                                        insgesamt also --&amp;gt; 128 + 2 = +130&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Mehr als 8 Bit mit Vorzeichen=====&lt;br /&gt;
Keine Sorge, das Vorzeichenbit gibt es bei allen binärzahlen immer nur einmal, an der höchsten Stelle. Wir haben also auch bei eine 8-Byte / 64-Bit Variablen nur ein einziges Mal das Problem.&lt;br /&gt;
&lt;br /&gt;
*Das Vorzeichenbit ist immer das MSB (höchst aussagekräftige Bit = most significant Bit). Zwei 32 Bit sieht das so aus:&lt;br /&gt;
 0b00000000000000000000000111111111 = hex 000001FF = dezimal +511&lt;br /&gt;
 0b11111111111111111111110000000001 = hex FFFFFC01 = dezimal -1023&lt;br /&gt;
Das Vorzeichenbit ist das ganz links (2^^31) &lt;br /&gt;
&lt;br /&gt;
Addiert werden solche Zahlen erstmal ganz normal &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dim Vala As Long&lt;br /&gt;
Dim Valb As Long&lt;br /&gt;
Dim Stat As Byte&lt;br /&gt;
&lt;br /&gt;
      Vala = 511&lt;br /&gt;
      Valb = -1023&lt;br /&gt;
      Print Hex(vala) ; &amp;quot;+&amp;quot; ; Hex(valb) ; &amp;quot;=&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
      $asm&lt;br /&gt;
      lds      r16, {vala}&lt;br /&gt;
      lds      r17, {vala+1}&lt;br /&gt;
      lds      r18, {vala+2}&lt;br /&gt;
      lds      r19, {vala+3}&lt;br /&gt;
&lt;br /&gt;
      lds      r20, {valb}&lt;br /&gt;
      lds      r21, {valb+1}&lt;br /&gt;
      lds      r22, {valb+2}&lt;br /&gt;
      lds      r23, {valb+3}&lt;br /&gt;
&lt;br /&gt;
      add      r16, r20            'das erste Byte noch ohne Carry&lt;br /&gt;
      adc      r17, r21            'der Rest mit Carry&lt;br /&gt;
      adc      r18, r22&lt;br /&gt;
      adc      r19, r23&lt;br /&gt;
&lt;br /&gt;
      in       r20, sreg           ' Wir holen uns die Status-Bits&lt;br /&gt;
      sts      {stat}, r20         ' speichern&lt;br /&gt;
&lt;br /&gt;
      sts      {Vala}, r16         ' speichern Ergebnis&lt;br /&gt;
      sts      {Vala+1}, r17&lt;br /&gt;
      sts      {Vala+2}, r18&lt;br /&gt;
      sts      {Vala+3}, r19&lt;br /&gt;
&lt;br /&gt;
      $end Asm&lt;br /&gt;
      Print Hex(vala) ; &amp;quot; &amp;quot; ; Bin(stat) ; &amp;quot; &amp;quot; ; Str(vala)            ' Zum Anschauen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Wir sehen, gesetzt wird von der ALU letzlich: &lt;br /&gt;
 S=1 und N=1&lt;br /&gt;
Also ist die Zahl negativ (S=1) und muß als 2-er Komplement verstanden werden.&lt;br /&gt;
&lt;br /&gt;
===Das Carry-Bit beim Bit-Verschieben===&lt;br /&gt;
Häufig wird das Carry-Bit auch als Zwischenlager und Anzeige bei den Schiebe- und Rotationsbefehlen. &lt;br /&gt;
&lt;br /&gt;
Es verhält sich da wie ein 9.Bit zu den 8-Bits im Register. &lt;br /&gt;
&lt;br /&gt;
Die Links- und Rechts-Schiebebefehle sind völlig symmetrisch&lt;br /&gt;
====LSL &amp;amp; ROL====&lt;br /&gt;
[[Bild:Left.png]]&lt;br /&gt;
 LSL register &lt;br /&gt;
 ROL register&lt;br /&gt;
Die Bits im Register werden bei beiden Befehlen eine Stelle nach links verschoben, das höchste Bit wandert in das Carry-Bit. &lt;br /&gt;
*Bei LSL wird eine NULL in das niederwertigste Bit des Registers reingeschoben&lt;br /&gt;
*Bei ROL wird das Carry-Bit (von vorher) in dieses Bit gestellt.&lt;br /&gt;
&lt;br /&gt;
====LSR &amp;amp; ROR====&lt;br /&gt;
[[Bild:Right.png]]&lt;br /&gt;
 LSR register &lt;br /&gt;
 ROR register&lt;br /&gt;
Die Bits im Register werden bei beiden Befehlen eine Stelle nach rechts verschoben, das niederste Bit wandert in das Carry-Bit. &lt;br /&gt;
*Bei LSR wird eine NULL in das höchste Bit des Registers reingeschoben&lt;br /&gt;
*Bei ROR wird das Carry-Bit (von vorher) in dieses Bit gestellt.&lt;br /&gt;
&lt;br /&gt;
====Anwendungen====&lt;br /&gt;
*Multiplizieren mit 2, 4, 8,... &lt;br /&gt;
Bei einem Byte ist da wohl nicht viel dazu zu sagen. &lt;br /&gt;
 LDI   register, &amp;amp;H05   ' register = &amp;amp;H05   = dezimal 5&lt;br /&gt;
 LSL   register         ' register = &amp;amp;H0A   = dezimal 10&lt;br /&gt;
Bei mehreren Bytes, also Variablen mit mehr als 8 Bit, beginnt man immer mit dem niederwertigsten Byte, der erste Befehl OHNE Carry, damit nicht irgendetwas ungewolltes reinrutscht, und dann weiter mit den anderen Bytes&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  LDS  register, {long_variable }    ' einlesen&lt;br /&gt;
  LSL  register                      ' verschieben, 2^^7 kommt ins Carry, bleibt&lt;br /&gt;
                                     ' 0 kommt nach 2^^0&lt;br /&gt;
  STS  {long_variable }, register    ' speichern (ändert nix im SREG)&lt;br /&gt;
&lt;br /&gt;
  LDS  register, {long_variable +1 }    ' einlesen (ändert auch nix im SREG)&lt;br /&gt;
  ROL  register                         ' verschieben, &lt;br /&gt;
                                        ' carry von vorher nach 2^^0&lt;br /&gt;
                                        ' 2^^7 ins Carry, bleibt&lt;br /&gt;
  STS  {long_variable + 1}, register    ' speichern &lt;br /&gt;
    .usw. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Dividieren durch 2, 4, 8,... &lt;br /&gt;
Da ist eben alles genau umgekehrt. Beginnen mit dem höchsten, sonst wie gehabt &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  LDS  register, {long_variable + 3}    &lt;br /&gt;
  LSR  register                         ' verschieben, 2^^0 kommt ins Carry und bleibt&lt;br /&gt;
                                        ' 0 kommt nach 2^^7&lt;br /&gt;
  STS  {long_variable +3}, register       &lt;br /&gt;
&lt;br /&gt;
  LDS  register, {long_variable + 2 }    &lt;br /&gt;
  ROR  register                         ' verschieben, &lt;br /&gt;
                                        ' carry von vorher nach 2^^7&lt;br /&gt;
                                        ' 2^^0 ins Carry&lt;br /&gt;
  STS  {long_variable + 2}, register    &lt;br /&gt;
    .usw. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Anwendung Lauflicht====&lt;br /&gt;
Ist auch beliebt. Angenommen, wir haben auf PORTB 8 LED hängen und möchten da ein Lauflicht haben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $asm&lt;br /&gt;
    LDI    r24, &amp;amp;HFF&lt;br /&gt;
    !OUT   DDRB, R24           ' wir setzen das Port auf OUTPUT&lt;br /&gt;
    LDI    r16, &amp;amp;H00           ' das &amp;quot;SchiebeByte&amp;quot; auf NULL&lt;br /&gt;
    SEC                        ' wir setzen das Carry Bit, damit ein 1-er da ist &lt;br /&gt;
Dauerschleife:&lt;br /&gt;
    ROL    r16                 ' Beim ersten Mal rutscht das carry-Bit rein&lt;br /&gt;
                               ' danach geht der 1-er automatisch im Kreis&lt;br /&gt;
    !OUT   PORTB, R16          ' wir legen das Byte mit dem einen 1-er an das Port&lt;br /&gt;
                               '---------------------------------------------------&lt;br /&gt;
                               ' Hier muß aber eine Verzögerung rein, sonst ist das &lt;br /&gt;
                               ' viel zu schnell&lt;br /&gt;
                               '---------------------------------------------------&lt;br /&gt;
    RJMP   Dauerschleife       ' und immer weiter&lt;br /&gt;
    $end asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Schreiben wir&lt;br /&gt;
     ROR    r16&lt;br /&gt;
geht's in die andere Richtung&lt;br /&gt;
&lt;br /&gt;
==Ecken, Kanten und Tücken==&lt;br /&gt;
Das AVR-Instruction-Set weist einige Feinheiten auf, die schon manchen Assembler-Roboter auf Kollisionskurs gebracht haben. &lt;br /&gt;
&lt;br /&gt;
Grad' dieses Kapitel kann ich nur nach und nach auffüllen, weil mir auch nicht immer gleich alles so einfällt. Vielleicht gibt's aber auch ein paar andere Assembleure, die ihren Erfahrungsschatz hier einbringen. &lt;br /&gt;
&lt;br /&gt;
Ich kann nur raten, beim Programmieren immer zumindest das &amp;quot;Complete Instruction Set Summary&amp;quot; bei der Hand zu haben. Immer wieder nachsehen, ob ein gerade ausgewählter Befehl das macht, was man sich vorstellt, und vor allem, ob er auch alle die Status-Register-Flags so setzt oder löscht, die man braucht. &lt;br /&gt;
&lt;br /&gt;
===INC/DEC===&lt;br /&gt;
Für Schleifenzähler etc. werden ja gerne &amp;quot;INC&amp;quot; (register +1) oder &amp;quot;DEC&amp;quot; (register -1) verwendet. Mit einem Vergleich auf einen Wert kontrolliert man dann diese Schleife. Ist ja auch ok. &lt;br /&gt;
&lt;br /&gt;
Muß man den Schleifenzähler aber nun so erweitern, daß man mehr als ein Byte braucht, muß man aufpassen. &lt;br /&gt;
&lt;br /&gt;
'''INC od. DEC   kümmern sich NICHT um das Carry-Bit !'''  &lt;br /&gt;
&lt;br /&gt;
*wer also schreibt&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 clr r0        (manche nehmen das gerne als Register, in dem immer NULL steht. Ist praktisch)&lt;br /&gt;
 ....&lt;br /&gt;
 INC r16&lt;br /&gt;
 adc r17, r0 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
kann sein blaues Wunder erleben. &lt;br /&gt;
&lt;br /&gt;
Da INC bei einem Überlauf einfach wieder bei NULL weitermacht, das Carry-Bit aber nicht anrührt, addieren wir im &amp;quot;ADC&amp;quot; Befehl ein Carry-Bit dazu, das von irgendeinem Befehl vorher übriggeblieben ist.&lt;br /&gt;
&lt;br /&gt;
Für ein sauberes Carry-Bit muß man also auf einen &amp;quot;echten&amp;quot; Additionsbefehl umsteigen. Und was sehen wir ?&lt;br /&gt;
&lt;br /&gt;
'''Es gibt keinen &amp;quot;ADDI&amp;quot; Befehl'''  ( für sowas wie &amp;quot;addi register, 1&amp;quot; )&lt;br /&gt;
&lt;br /&gt;
Wenn wir also kein Register zur Hand haben, in das wir einen 1-er reinschreiben können, müssen wir&lt;br /&gt;
 SUBI register, -1 &lt;br /&gt;
schreiben. Ist klar:  register - (-1) = register + 1, also das, was wir brauchen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
 SUBI    r16, -1        &lt;br /&gt;
 SBCI    r17, -1            (abziehen festen Wert von Register MIT CARRY) &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wen die vielen &amp;quot;-1&amp;quot; wundern: Wir brauchen für eine 16 Bit Zahl (R16:R17) natürlich auch ein 16-Bit-tiges &amp;quot;-1&amp;quot; Literal. Und bei den Vorzeichen-Zahlen haben wir ja festgestellt, daß -1 ja so aussieht:&lt;br /&gt;
 bei  8-Bit 0b11111111                          &amp;amp;HFF&lt;br /&gt;
 bei 16-Bit 0b1111111111111111                  &amp;amp;HFFFF&lt;br /&gt;
 bei 32-Bit 0b11111111111111111111111111111111  &amp;amp;HFFFFFFFF&lt;br /&gt;
Und diese vielen Bits müssen wir auf die einzelnen Subtraktionsbefehlt eben verteilen &lt;br /&gt;
&lt;br /&gt;
*Hinaufzählen mit z.B. 32 Bit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
 SUBI    r16, -1        &lt;br /&gt;
 SBCI    r17, -1            &lt;br /&gt;
 SBCI    r18, -1            &lt;br /&gt;
 SBCI    r19, -1            &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Beim Dekrementieren ist eigentlich nur das Literal ein anderes: statt -1 eben +1, d.h., wir subtrahieren &amp;quot;wirklich&amp;quot;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
 SUBI    r16, 1        &lt;br /&gt;
 SBCI    r17, 0    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*32 Bit ? Richtig:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
 SUBI    r16, 1        &lt;br /&gt;
 SBCI    r17, 0            &lt;br /&gt;
 SBCI    r18, 0            &lt;br /&gt;
 SBCI    r19, 0            &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Ja, es gibt aber doch was für 16 Bit ?'''&lt;br /&gt;
 ADIW  register:register+1 ,  nn  ' nn auf die 16-bit von register:register+1 addieren &lt;br /&gt;
 SBIW  register:register+1 ,  nn  ' nn von den 16-bit von register:register+1 subtrahieren&lt;br /&gt;
(''nn'' kann sein 0 - 63)&lt;br /&gt;
&lt;br /&gt;
Ja, aber das geht nur mit den Registerpaaren R24:R25, R26:R27, R28:R29, R30:R31&lt;br /&gt;
&lt;br /&gt;
Da die Paare R26:R27, R28:R29, R30:R31 aber gleichzeitig die einzigen Pointer-Register sind (dazu kommen wir noch, aber grad in Schleifen braucht man die meist für was anderes)&lt;br /&gt;
&lt;br /&gt;
'''Bleibt eigentlich nur R24:R25 für solche 16-Bit Befehle'''&lt;br /&gt;
&lt;br /&gt;
==Unterprogramme==&lt;br /&gt;
Man sieht ja schon an den bisherigen Beispielen, daß gewisse Befehlsfolgen immer wieder benötigt werden. &lt;br /&gt;
*Typisches Beispiel, was man ja schon beim ersten Lauflicht braucht, sind Verzögerungsroutinen, damit die LEDs nicht gar so schnell funzeln. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man überall Zählschleifen einbauen, die auf diese Art das Programm gewissermaßen ausbremsen. Aber das verbraucht dann doch mächtig Platz, und auch nur die geringste Änderung in der Befehlsfolge muß man x-mal machen. &lt;br /&gt;
&lt;br /&gt;
Wenn man also diese Befehlsfolge einmal zusammenfaßt und irgendwo außerhalb der Haupt-Programm-Schleife deponiert und mit einem Namen versieht, könnte man doch immer, wenn man sie braucht, diese Folge aufrufen. Ja, aber wie findet man dann wieder zurück, um dort weiterzumachen, wo man grad war?&lt;br /&gt;
&lt;br /&gt;
*Ganz einfach:&lt;br /&gt;
===CALL &amp;amp; RET===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hauptschleife:&lt;br /&gt;
    .....&lt;br /&gt;
    .....&lt;br /&gt;
    CALL   Befehlsfolge&lt;br /&gt;
    .....&lt;br /&gt;
    CALL   Befehlsfolge&lt;br /&gt;
    .....&lt;br /&gt;
    .....&lt;br /&gt;
    JMP   Hauptschleife&lt;br /&gt;
&lt;br /&gt;
Befehlsfolge:&lt;br /&gt;
    ....&lt;br /&gt;
    RET  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das sind zwei listige Erweiterungen von &amp;quot;JMP&amp;quot;:&lt;br /&gt;
*CALL heißt: Erst merken, wo wir grad sind, und dann JMP irgendwohin&lt;br /&gt;
*RET heißt: Mach &amp;quot;JMP&amp;quot; auf die Stelle, die man sich gemerkt hat (und vergiß sie dann)&lt;br /&gt;
&lt;br /&gt;
Super, kann man gut brauchen, aber wie macht der AVR das ? &lt;br /&gt;
&lt;br /&gt;
Dazu braucht der AVR zwei Dinge:  Einen &amp;quot;Stapel&amp;quot; und einen &amp;quot;Stapelzeiger&amp;quot;. &lt;br /&gt;
*Den &amp;quot;Stapelzeiger&amp;quot; (englisch &amp;quot;Stackpointer&amp;quot;) hat er schon in der CPU eingebaut. &lt;br /&gt;
*Der &amp;quot;Stapel&amp;quot; (&amp;quot;Stack&amp;quot;) ist ein Stück Speicher, den uns Bascom bei seiner Initialisierung vom oberen Ende des SRAM abgeknöpft hat. &lt;br /&gt;
**Am Anfang enthält der Stackpointer die oberste Adresse vom Stack&lt;br /&gt;
**Jedesmal, wenn sich der AVR eine Rücksprungadresse merken soll, schreibt er sie dorthin, wo der Stackpointer grad hinzeigt, und zieht dann 2 (so lange ist eine solche Adresse) vom Stackpointer ab.&lt;br /&gt;
**Geht es jetzt ans &amp;quot;RET&amp;quot;, addiert er jetzt diese 2 wieder auf den Pointer, und springt zu der Adresse, die dort steht. &lt;br /&gt;
**Sagt man nun aber nicht &amp;quot;RET&amp;quot;, sondern nochmal &amp;quot;CALL&amp;quot;, schreibt er diese (neue) Adresse nun UNTER die vorherige (und zieht wieder 2 vom Pointer ab). Im Stack stehen jetzt also beide Adressen untereinander, der Stack ist größer geworden &lt;br /&gt;
&lt;br /&gt;
*Der Speicher ist also ein armer Hund: Mit jedem &amp;quot;CALL&amp;quot; knabbern wir von oben was weg, und mit jedem &amp;quot;DIM&amp;quot; (im Bascom) von unten. &lt;br /&gt;
 Erschütternde Tragödien spielen sich ab, wenn sich der Stack und das &amp;quot;DIM&amp;quot; Bereich &lt;br /&gt;
 mal irgendwo in der Mitte treffen, die beiden Bereiche sind sich nämlich spinnefeind.&lt;br /&gt;
Beim Bascom selbst wird es noch viel früher dramatisch: Der verlangt nämlich, daß wir schon beim Programmieren schätzen, wie groß der Stack wohl werden wird ($HWSTACK=nn).&lt;br /&gt;
&lt;br /&gt;
[[Bascom_Inside#Stacks_.26_Frame|Da gibt's ein paar Details dazu]]&lt;br /&gt;
&lt;br /&gt;
===PUSH &amp;amp; POP===&lt;br /&gt;
Mit diesen Befehlen wird der Stack und der Stackpointer direkt angesprochen. Denn &amp;quot;CALL &amp;amp; RET&amp;quot; sind nicht die einzigen, die das brauchen. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 PUSH  register      'Den Inhalt von register an die Adresse schreiben, an die der Stackpointer&lt;br /&gt;
                     'grad hinzeigt, und dann 1 vom Pointer abziehen&lt;br /&gt;
 POP   register      'Auf den Stackpointer 1 addieren, und das Byte dort ins register laden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Typische Anwendung:&lt;br /&gt;
In unserem &amp;quot;Unterprogramm&amp;quot; müssen wir irgendwelche Register verändern, wir wollen aber nicht, daß das Hauptprogramm, das uns aufgerufen hat, was davon merkt&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Befehlsfolge:&lt;br /&gt;
    PUSH  R24&lt;br /&gt;
    LDI   R24, 127&lt;br /&gt;
_Schleife:&lt;br /&gt;
    DEC   R24&lt;br /&gt;
    BRNE  _Schleife&lt;br /&gt;
    POP   R24&lt;br /&gt;
    RET  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nach dem &amp;quot;RET&amp;quot; hat R24 also wieder den Inhalt, den es vorher hatte.&lt;br /&gt;
==Interrupt-Routinen (ISR)==&lt;br /&gt;
ISR = &amp;quot;Interrupt Service Request&amp;quot; = &amp;quot;Unterbrechungs-Behandlung-Anforderung&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Ein AVR besteht ja nicht nur aus CPU und Speicher, sondern auch aus einer Reihe von &amp;quot;Geräten&amp;quot;, die ihre Funktion völlig unabhängig durchführen, wenn man sie mal konfiguriert hat. &lt;br /&gt;
&lt;br /&gt;
Aber immer, wenn sie grad ihre Meß- oder Zählfunktion erledigt haben, wollen sie ihr Ergebnis irgendwie mitteilen und dafür wissen, wie's weitergehen soll. &lt;br /&gt;
&lt;br /&gt;
Wir können dazu mit dem AVR einen Deal machen: &lt;br /&gt;
*Wir schreiben ein Unterprogramm, daß seine dahingehenden Ansprüche erfüllt und sagen dem AVR, wo es im Programmspeicher steht.&lt;br /&gt;
*Und dafür paßt der AVR auf das &amp;quot;Gerät&amp;quot; auf und macht den &amp;quot;Call&amp;quot; auf dieses Unterprogramm völlig automatisch genau dann, wenn es soweit ist. Wir brauchen uns im &amp;quot;normalen&amp;quot; Programm nun nicht mehr darum zu kümmern&lt;br /&gt;
&lt;br /&gt;
Um so einen Handel einzufädeln, ist es wirklich praktisch, wenn wir Bascom hinzuziehen. Der kennt nämlich die meisten AVR-Controllertypen gewissermaßen persönlich und weiß am besten, an welchen Strippen man da bei irgendeinem Controller ziehen muß. &lt;br /&gt;
===Beispiel Timer===&lt;br /&gt;
Sagen wir, wir wolle jede Millisekunde ein Unterprogramm durchführen (lassen). &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
$hwstack = 48&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kennen wir an sich schon. Aber wenn wir Interrupts verwenden, sollten wir bei &amp;quot;$HWSTACK=&amp;quot; schon ein bißchen was spendieren. &lt;br /&gt;
&lt;br /&gt;
*Jetzt lassen wir Bascom den Timer konfigurieren&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Config Timer0 = Timer , Prescale = 64      'Timer Setup&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lassen wir das Bascom machen. Für uns ist es dann nämlich egal, ob das auf einem Tiny oder einem ATmega128 laufen wird, es schaut immer gleich aus. &lt;br /&gt;
&lt;br /&gt;
*Jetzt der Deal: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   On Timer0 Interrupt_ticker       ' Das ist das Unterprogramm (heißt jetzt aber ISR-Routine) &lt;br /&gt;
&lt;br /&gt;
   Enable Timer0                    ' jetzt machen wir den Timer scharf &lt;br /&gt;
&lt;br /&gt;
   Enable Interrupts                ' Und das ist sozusagen der Hauptschalter für sowas&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Das &amp;quot;normale&amp;quot; Programm: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   $asm&lt;br /&gt;
Hauptschleife:&lt;br /&gt;
   '------------------------------&lt;br /&gt;
   ' Irgendein (Assembler?) Programm, dem der Timer völlig schnuppe ist&lt;br /&gt;
   '------------------------------&lt;br /&gt;
   JMP Hauptschleife &lt;br /&gt;
   $end asm&lt;br /&gt;
&lt;br /&gt;
END&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Und nun die ISR-Routine&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Interrupt_ticker:             &lt;br /&gt;
&lt;br /&gt;
   Timer0 = 131            ' Damit der arme Counter nicht immer von Null weg zählen muß&lt;br /&gt;
                           ' er darf schon mit 131 anfangen&lt;br /&gt;
&lt;br /&gt;
   $asm&lt;br /&gt;
   '------------------------------&lt;br /&gt;
   ' Die befehle, die wir jede mS ausführen wollen &lt;br /&gt;
   '------------------------------&lt;br /&gt;
&lt;br /&gt;
   $end asm&lt;br /&gt;
&lt;br /&gt;
   Return                  ' wieder zurück und weiter im normalen Programm &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die hier verwendeten Werte von &amp;quot;64&amp;quot; für den Prescaler und die &amp;quot;131&amp;quot; für den Counter sind aus der Angabe &amp;quot;$crystal = 8000000&amp;quot; berechnet worden und ergeben Interrupts im Abstand von 1 mS.&lt;br /&gt;
&lt;br /&gt;
==Autor==&lt;br /&gt;
[[User:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
*[[AVR_Assembler_Einf%C3%BChrung|AVR Assembler Einführung]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
*[[Bascom: Debounce ISR in Assembler]]&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13871</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13871"/>
				<updated>2008-08-16T11:21:41Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Sample Code Variante 2: übersichtlich mit ADR2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly [[http://www.mikrocontroller.net/articles/AVR_Butterfly]] (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesamten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
Als Beispiel wird hier die um die Umsetzung '''des Menüs aus dem AVR Butterfly Evaluation Kit in Bascom''' gezeigt:&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 1: Umsetzung des Codes aus dem AVR Butterfly Evaluation Kit ==&lt;br /&gt;
Der Code ist die 1:1 Umsetzung des C-Codes aus dem AVR Butterfly Evaluation Kit.&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
* BASCOM 1.11.8.7 [Probleme mir der LCD Darstellung]&lt;br /&gt;
* BASCOM 1.11.8.1 [OK]&lt;br /&gt;
&lt;br /&gt;
Die Codelänge des Samples beträgt ca. 2300Byte. Der größte Teil geht für die LCD-Anzeige, Anzeigetexte etc. drauf. Der State Machine selber ist sehr schlank, da nur Pointer (Zeiger) gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 2: übersichtlich mit ADR2 ==&lt;br /&gt;
Die Variante 1 ist leider unübersichtlich, da 3 Tabellen getrennt geführt werden (die State Machine, die Menütexte und die zugehörigen Unterprogrammeinsprünge).&lt;br /&gt;
Nachfolgend eine Variante, in der nur eine Tabelle geführt wird. Die Lösung wurde möglich, nachdem in Bascom in der Version 1.11.8.9 den Befehl ADR / ADR2 eingeführt wurde.&lt;br /&gt;
&lt;br /&gt;
In jedem Datensatz stehen jetzt die Daten für die Verzweigung der State Machine (inf. Tastatureingabe), der Pointer für den Unterprogrammeinsprung und der Menütext zusammen.&lt;br /&gt;
Die Codelänge das Beispiel liegt gleichauf mit der Variante 1. Dafür ist das Menü deutlich einfacher zu editieren.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
Die Bedienung erfolgt wie bei Variante 1.&lt;br /&gt;
&lt;br /&gt;
Da jetzt zur Vereinfachung Nummern für die States geführt werden, empfiehlt es sich ein Plan (siehe Grafik oben) anzulegen und jeden Kasten mit einer Nummer zu versehen. Die Zuordnung der Status-Nummern entsprechend der Tasten-Verzweigung (UP/DOWN etc.) ist dann schnell abgelesen. Alternativ kann man natürlich beliebige Labelnamen verwenden. &lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.9.1&lt;br /&gt;
'  Codelänge 2028 Byte&lt;br /&gt;
&lt;br /&gt;
$regfile = &amp;quot;m8def.dat&amp;quot;&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
$sim                                                        'empty wait loops - faster simulation - not for real world!!&lt;br /&gt;
&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in dem blauen &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code:&lt;br /&gt;
Const Key_buttons = 4                                       'Anzahl der Tasten&lt;br /&gt;
Dim Keycode_string As String * Key_buttons&lt;br /&gt;
'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
Keycode_string = &amp;quot;{056}{050}{052}{054}&amp;quot;                     'see scan codes in ASCII chart&lt;br /&gt;
Dim Keycode(key_buttons) As Byte At Keycode_string Overlay&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
'******** LCD Settings ***********************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcd = 16 * 1&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim State As Word                                           'aktueller State&lt;br /&gt;
Dim State_renew As Byte                                     'Flag&lt;br /&gt;
Dim State_gosub As Word                                     'aktuelles Unterprogramm&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State_renew = 1&lt;br /&gt;
State = Loadlabel(s10)                                      'Startbildschirm&lt;br /&gt;
Key = Key_null                                              'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim W As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
    'Menüeintrag und Tastencodes finden und ggf. State wechseln&lt;br /&gt;
    'hier nur Tastendruck auswerten&lt;br /&gt;
    Gosub Change_state&lt;br /&gt;
    'Pointer nach Statuswechsel neu setzen&lt;br /&gt;
    If State_renew = 1 Then Gosub Change_state&lt;br /&gt;
&lt;br /&gt;
    'Unterprogramm des aktuellen State anspringen&lt;br /&gt;
    LDS R31, {State_gosub+1}                                'High see Bascom-Doc Mixing ASM and BASIC&lt;br /&gt;
    LDS R30, {State_gosub}                                  'Low&lt;br /&gt;
    LSR R31                                                 'Division durch 2 (Alternativ ADR verwenden)&lt;br /&gt;
    ROR R30&lt;br /&gt;
    'Call zum Sub&lt;br /&gt;
    ICALL&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Tastaturabfrage mit Halt nur für Simulator, ansonsten Sleep+Timer_ISR verwenden!!&lt;br /&gt;
    Key = Waitkey()&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* State routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Change_state&lt;br /&gt;
'Call from:  main loop&lt;br /&gt;
'Purpose:    Status der State Machine feststellen und ggf. Wechseln&lt;br /&gt;
'Result:     Pointer auf State / Variable State_renew&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Change_state:&lt;br /&gt;
    lds R8, {State}&lt;br /&gt;
    lds R9, {State + 1}&lt;br /&gt;
    For I = 1 To Key_buttons&lt;br /&gt;
      Read W&lt;br /&gt;
      If Key = Keycode(i) Then&lt;br /&gt;
           If W &amp;lt;&amp;gt; Loadlabel(null) Then&lt;br /&gt;
              State_renew = 1&lt;br /&gt;
              Key = Key_null                                'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
              State = W&lt;br /&gt;
           End If&lt;br /&gt;
      End If&lt;br /&gt;
    Next I&lt;br /&gt;
    Read State_gosub                                        'Adresse des akt. Unterprogramms einlesen&lt;br /&gt;
    Read Lcd_textbuffer                                     'read LCD text&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Change_state_by_sub&lt;br /&gt;
'Call from:  subroutine&lt;br /&gt;
'Purpose:    change state of state machine by a subbroutine&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Change_state_by_sub:&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
   Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'DATA:       State Machine&lt;br /&gt;
'Result:     Pointers auf States , Unterprogramme&lt;br /&gt;
'            Menütexte&lt;br /&gt;
'Format:     1. Zeile: für jede Taste genau eine Verzeigung ADR2 (see Key_buttons=xx)&lt;br /&gt;
'            2. Zeile: Sprunglabel als ADR2 für ein ggf. anzuspringendes Unterprogramm&lt;br /&gt;
'            3. Zeile: Displaytext des akt. Status als DATA Feld&lt;br /&gt;
'Hinweis:    wenn nichts passieren soll wird das Label NULL eingetragen&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Null:&lt;br /&gt;
         'Null is a dummy flag for State and Gosub -&amp;gt; do nothing&lt;br /&gt;
         Return&lt;br /&gt;
&lt;br /&gt;
S10:&lt;br /&gt;
         Adr2 Null : Adr2 S20 : Adr2 Null : Adr2 S11        'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
         Adr2 Null                                          'Subroutine for current State&lt;br /&gt;
         Data &amp;quot;1 Butterfly Bascom&amp;quot;                          'Menue Display Text&lt;br /&gt;
S11:&lt;br /&gt;
             Adr2 Null : Adr2 Null : Adr2 S10 : Adr2 Null   'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;11 Rev 2&amp;quot;&lt;br /&gt;
S20:&lt;br /&gt;
         Adr2 S10 : Adr2 S30 : Adr2 Null : Adr2 S21&lt;br /&gt;
         Adr2 Null&lt;br /&gt;
         Data &amp;quot;2 Time&amp;quot;&lt;br /&gt;
S21:&lt;br /&gt;
             Adr2 Null : Adr2 S22 : Adr2 S20 : Adr2 S23&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
S22:&lt;br /&gt;
             Adr2 S21 : Adr2 Null : Adr2 S20 : Adr2 S24&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;22 Date&amp;quot;&lt;br /&gt;
S23:                                                        'Show HH:MM:SS&lt;br /&gt;
                   Adr2 Null : Adr2 S24 : Adr2 S21 : Adr2 S25&lt;br /&gt;
                   Adr2 Showclock&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S24:                                                        'Show DD:MM:YY&lt;br /&gt;
                   Adr2 S23 : Adr2 Null : Adr2 S22 : Adr2 S26&lt;br /&gt;
                   Adr2 Showdate&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S25:&lt;br /&gt;
                   Adr2 S27 : Adr2 Null : Adr2 S23 : Adr2 S65&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
S26:&lt;br /&gt;
                   Adr2 Null : Adr2 S28 : Adr2 S24 : Adr2 S66&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
S27:&lt;br /&gt;
                   Adr2 Null : Adr2 S25 : Adr2 S23 : Adr2 S67&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
S28:&lt;br /&gt;
                   Adr2 S26 : Adr2 Null : Adr2 S23 : Adr2 S68&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
S30:&lt;br /&gt;
         Adr2 S20 : Adr2 S40 : Adr2 Null : Adr2 S31&lt;br /&gt;
         Adr2 Null&lt;br /&gt;
         Data &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
S31:&lt;br /&gt;
             Adr2 Null : Adr2 S32 : Adr2 S30 : Adr2 S35&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
S32:&lt;br /&gt;
             Adr2 S31 : Adr2 S33 : Adr2 S30 : Adr2 S36&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
S33:&lt;br /&gt;
             Adr2 S32 : Adr2 S34 : Adr2 S30 : Adr2 S37&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
S34:&lt;br /&gt;
             Adr2 S33 : Adr2 Null : Adr2 S30 : Adr2 S38&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
S35:                                                        'HH:MM&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S31 : Adr2 Null&lt;br /&gt;
                   Adr2 Datalogger_setloginterval&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S36:&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S32 : Adr2 S76&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
S37:                                                        '1234&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S33 : Adr2 Null&lt;br /&gt;
                   Adr2 Datalogger_logcount&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S38:&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S34 : Adr2 S78&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
S40:&lt;br /&gt;
        Adr2 S30 : Adr2 S50 : Adr2 Null : Adr2 S41&lt;br /&gt;
        Adr2 Null&lt;br /&gt;
        Data &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
S41:&lt;br /&gt;
           Adr2 Null : Adr2 S42 : Adr2 S40 : Adr2 S45&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
S42:&lt;br /&gt;
           Adr2 S41 : Adr2 S43 : Adr2 S40 : Adr2 S46&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
S43:&lt;br /&gt;
           Adr2 S42 : Adr2 S44 : Adr2 S40 : Adr2 S47&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
S44:&lt;br /&gt;
           Adr2 S43 : Adr2 Null : Adr2 S40 : Adr2 S48&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
S45:                                                        '+24°C&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S41 : Adr2 Null&lt;br /&gt;
                   Adr2 Temperaturefunc&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S46:                                                        '0 mV&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S42 : Adr2 Null&lt;br /&gt;
                   Adr2 Voltagefunc&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S47:                                                        'CH:RAW&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S43 : Adr2 Null&lt;br /&gt;
                   Adr2 Adc_raw_func&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S48:                                                        '2900mV&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S44 : Adr2 Null&lt;br /&gt;
                   Adr2 Adc_batt_func&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S50:&lt;br /&gt;
        Adr2 S40 : Adr2 Null : Adr2 Null : Adr2 S51&lt;br /&gt;
        Adr2 Null&lt;br /&gt;
        Data &amp;quot;5 Options&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S51:&lt;br /&gt;
           Adr2 Null : Adr2 S52 : Adr2 S50 : Adr2 S56&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
S52:&lt;br /&gt;
           Adr2 S51 : Adr2 S53 : Adr2 S50 : Adr2 S57&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S53:&lt;br /&gt;
           Adr2 S52 : Adr2 S54 : Adr2 S50 : Adr2 S83&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S54:&lt;br /&gt;
           Adr2 S53 : Adr2 S55 : Adr2 S50 : Adr2 S58&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S55:&lt;br /&gt;
           Adr2 S54 : Adr2 Null : Adr2 S50 : Adr2 S59&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
S56:                                                        '0...15&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S51 : Adr2 Null&lt;br /&gt;
                   Adr2 Setcontrast&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S57:&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S52 : Adr2 S87&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S58:                                                        'OFF/5..90min&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S54 : Adr2 Null&lt;br /&gt;
                   Adr2 Autopower&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S59:                                                        'ON/OFF&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S55 : Adr2 Null&lt;br /&gt;
                   Adr2 Keyclick_set&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenüs zu State 25 bis 28&lt;br /&gt;
S65:                                                        'sub for &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
                          Adr2 S67 : Adr2 Null : Adr2 S23 : Adr2 Null&lt;br /&gt;
                          Adr2 Setclock&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S66:                                                        'sub for &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
                          Adr2 Null : Adr2 S68 : Adr2 S24 : Adr2 Null&lt;br /&gt;
                          Adr2 Setdate&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S67:                                                        'sub for &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
                          Adr2 Null : Adr2 S65 : Adr2 S23 : Adr2 Null&lt;br /&gt;
                          Adr2 Setclockformat&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S68:                                                        'sub for  &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
                          Adr2 S66 : Adr2 Null : Adr2 S24 : Adr2 Null&lt;br /&gt;
                          Adr2 Setdateformat&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenüs zu State 36 und 38&lt;br /&gt;
S76:                                                        'sub for  &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
                          Adr2 Null : Adr2 Null : Adr2 S36 : Adr2 Null&lt;br /&gt;
                          Adr2 Datalogger_erase&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S78:                                                        'sub for  &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
                          Adr2 Null : Adr2 Null : Adr2 S38 : Adr2 Null&lt;br /&gt;
                          Adr2 Datalogger_rs232&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenü zu State 53 und 57&lt;br /&gt;
S83:                                                        'sub for  &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
                          Adr2 Null : Adr2 Null : Adr2 S53 : Adr2 Null&lt;br /&gt;
                          Adr2 Power_off_func&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S87:                                                        'sub for &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
                          Adr2 Null : Adr2 Null : Adr2 S57 : Adr2 Null&lt;br /&gt;
                          Adr2 Bootfunc&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
    State_renew = 0&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = Loadlabel(s32)                                    'St_datalogger_erase&lt;br /&gt;
  Gosub Change_state_by_sub&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = Loadlabel(s34)                                   'St_datalogger_rs232 as next status of state machine&lt;br /&gt;
   Gosub Change_state_by_sub&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = Loadlabel(s10)                                   'St_avrbf as next status of state machine&lt;br /&gt;
   Gosub Change_state_by_sub&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = Loadlabel(s53)                                    'Return  State is St_options_power_off&lt;br /&gt;
  Gosub Change_state_by_sub&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]  mit Hinweisen zu einer [[Sourcevergleich#Bascom (State Machine)|State Machine in BASCOM]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
* AVR Butterfly Application code port to avr-gcc [[http://www.siwawi.arubi.uni-kl.de/avr_projects/#bf_app]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13749</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13749"/>
				<updated>2008-07-28T09:06:24Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: minor change for code var 2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly [[http://www.mikrocontroller.net/articles/AVR_Butterfly]] (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesamten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
Als Beispiel wird hier die um die Umsetzung '''des Menüs aus dem AVR Butterfly Evaluation Kit in Bascom''' gezeigt:&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 1: Umsetzung des Codes aus dem AVR Butterfly Evaluation Kit ==&lt;br /&gt;
Der Code ist die 1:1 Umsetzung des C-Codes aus dem AVR Butterfly Evaluation Kit.&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
* BASCOM 1.11.8.7 [Probleme mir der LCD Darstellung]&lt;br /&gt;
* BASCOM 1.11.8.1 [OK]&lt;br /&gt;
&lt;br /&gt;
Die Codelänge des Samples beträgt ca. 2300Byte. Der größte Teil geht für die LCD-Anzeige, Anzeigetexte etc. drauf. Der State Machine selber ist sehr schlank, da nur Pointer (Zeiger) gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 2: übersichtlich mit ADR2 ==&lt;br /&gt;
Die Variante 1 ist leider unübersichtlich, da 3 Tabellen getrennt geführt werden (die State Machine, die Menütexte und die zugehörigen Unterprogrammeinsprünge).&lt;br /&gt;
Nachfolgend eine Variante, in der nur eine Tabelle geführt wird. Die Lösung wurde möglich, nachdem in Bascom in der Version 1.11.8.9 den Befehl ADR / ADR2 eingeführt wurde.&lt;br /&gt;
&lt;br /&gt;
In jedem Datensatz stehen jetzt die Daten für die Verzweigung der State Machine (inf. Tastatureingabe), der Pointer für den Unterprogrammeinsprung und der Menütext zusammen.&lt;br /&gt;
Die Codelänge das Beispiel liegt gleichauf mit der Variante 1. Dafür ist das Menü deutlich einfacher zu editieren.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
Die Bedienung erfolgt wie bei Variante 1.&lt;br /&gt;
&lt;br /&gt;
Da jetzt zur Vereinfachung Nummern für die States geführt werden, empfiehlt es sich ein Plan (siehe Grafik oben) anzulegen und jeden Kasten mit einer Nummer zu versehen. Die Zuordnung der Status-Nummern entsprechend der Tasten-Verzweigung (UP/DOWN etc.) ist dann schnell abgelesen. Alternativ kann man natürlich beliebige Labelnamen verwenden. &lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.9.1&lt;br /&gt;
'  Codelänge 2242 Byte&lt;br /&gt;
&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in dem blauen &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code:&lt;br /&gt;
&lt;br /&gt;
Dim Keycode_string As String * 4&lt;br /&gt;
'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
Keycode_string = &amp;quot;{056}{050}{052}{054}&amp;quot;&lt;br /&gt;
Dim Keycode(4) As Byte At Keycode_string Overlay&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcd = 16 * 1&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim W As Word&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim State As Word                                           'aktueller State&lt;br /&gt;
Dim State_renew As Byte                                     'Flag&lt;br /&gt;
Dim State_gosub As Word                                     'aktuelles Unterprogramm&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State_renew = 1&lt;br /&gt;
State = Loadlabel(s10)                                      'Startbildschirm&lt;br /&gt;
Key = Key_null                                              'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
    'Menüeintrag und Tastencodes finden und ggf. State wechseln&lt;br /&gt;
    'hier nur Tastendruck auswerten&lt;br /&gt;
    Gosub Change_state&lt;br /&gt;
    'Pointer nach Statuswechsel neu setzen&lt;br /&gt;
    If State_renew = 1 Then Gosub Change_state&lt;br /&gt;
&lt;br /&gt;
    'Unterprogramm des aktuellen State anspringen&lt;br /&gt;
    LDS R31, {State_gosub+1}                                'High see Bascom-Doc Mixing ASM and BASIC&lt;br /&gt;
    LDS R30, {State_gosub}                                  'Low&lt;br /&gt;
    LSR R31                                                 'Division durch 2 (Alternativ ADR verwenden)&lt;br /&gt;
    ROR R30&lt;br /&gt;
    'Call zum Sub&lt;br /&gt;
    ICALL&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Tastaturabfrage mit Halt nur für Simulator, ansonsten Sleep+Timer_ISR verwenden!!&lt;br /&gt;
    Key = Waitkey()&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* State routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Change_state&lt;br /&gt;
'Call from:  main loop&lt;br /&gt;
'Purpose:    Status der State Machine feststellen und ggf. Wechseln&lt;br /&gt;
'Result:     Pointer auf State / Variable State_renew&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Change_state:&lt;br /&gt;
    lds R8, {State}&lt;br /&gt;
    lds R9, {State + 1}&lt;br /&gt;
    For I = 1 To 4&lt;br /&gt;
      Read W&lt;br /&gt;
      If Key = Keycode(i) Then&lt;br /&gt;
           If W &amp;lt;&amp;gt; Loadlabel(null) Then&lt;br /&gt;
              State_renew = 1&lt;br /&gt;
              Key = Key_null                                'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
              State = W&lt;br /&gt;
           End If&lt;br /&gt;
      End If&lt;br /&gt;
    Next I&lt;br /&gt;
    Read State_gosub                                        'Adresse des akt. Unterprogramms einlesen&lt;br /&gt;
    Read Lcd_textbuffer                                     'read LCD text&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Change_state_by_sub&lt;br /&gt;
'Call from:  subroutine&lt;br /&gt;
'Purpose:    change state of state machine by a subbroutine&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Change_state_by_sub:&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
   Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'DATA:       State Machine&lt;br /&gt;
'Result:     Pointers auf States , Unterprogramme&lt;br /&gt;
'            Menütexte&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Null:&lt;br /&gt;
         'Null is a dummy flag for State and Gosub -&amp;gt; do nothing&lt;br /&gt;
         Return&lt;br /&gt;
&lt;br /&gt;
S10:&lt;br /&gt;
         Adr2 Null : Adr2 S20 : Adr2 Null : Adr2 S11        'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
         Adr2 Null                                          'Subroutine for current State&lt;br /&gt;
         Data &amp;quot;1 Butterfly Bascom&amp;quot;                          'Menue Display Text&lt;br /&gt;
S11:&lt;br /&gt;
             Adr2 Null : Adr2 Null : Adr2 S10 : Adr2 Null   'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;11 Rev 2&amp;quot;&lt;br /&gt;
S20:&lt;br /&gt;
         Adr2 S10 : Adr2 S30 : Adr2 Null : Adr2 S21&lt;br /&gt;
         Adr2 Null&lt;br /&gt;
         Data &amp;quot;2 Time&amp;quot;&lt;br /&gt;
S21:&lt;br /&gt;
             Adr2 Null : Adr2 S22 : Adr2 S20 : Adr2 S23&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
S22:&lt;br /&gt;
             Adr2 S21 : Adr2 Null : Adr2 S20 : Adr2 S24&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;22 Date&amp;quot;&lt;br /&gt;
S23:                                                        'Show HH:MM:SS&lt;br /&gt;
                   Adr2 Null : Adr2 S24 : Adr2 S21 : Adr2 S25&lt;br /&gt;
                   Adr2 Showclock&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S24:                                                        'Show DD:MM:YY&lt;br /&gt;
                   Adr2 S23 : Adr2 Null : Adr2 S22 : Adr2 S26&lt;br /&gt;
                   Adr2 Showdate&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S25:&lt;br /&gt;
                   Adr2 S27 : Adr2 Null : Adr2 S23 : Adr2 S65&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
S26:&lt;br /&gt;
                   Adr2 Null : Adr2 S28 : Adr2 S24 : Adr2 S66&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
S27:&lt;br /&gt;
                   Adr2 Null : Adr2 S25 : Adr2 S23 : Adr2 S67&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
S28:&lt;br /&gt;
                   Adr2 S26 : Adr2 Null : Adr2 S23 : Adr2 S68&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
S30:&lt;br /&gt;
         Adr2 S20 : Adr2 S40 : Adr2 Null : Adr2 S31&lt;br /&gt;
         Adr2 Null&lt;br /&gt;
         Data &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
S31:&lt;br /&gt;
             Adr2 Null : Adr2 S32 : Adr2 S30 : Adr2 S35&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
S32:&lt;br /&gt;
             Adr2 S31 : Adr2 S33 : Adr2 S30 : Adr2 S36&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
S33:&lt;br /&gt;
             Adr2 S32 : Adr2 S34 : Adr2 S30 : Adr2 S37&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
S34:&lt;br /&gt;
             Adr2 S33 : Adr2 Null : Adr2 S30 : Adr2 S38&lt;br /&gt;
             Adr2 Null&lt;br /&gt;
             Data &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
S35:                                                        'HH:MM&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S31 : Adr2 Null&lt;br /&gt;
                   Adr2 Datalogger_setloginterval&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S36:&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S32 : Adr2 S76&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
S37:                                                        '1234&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S33 : Adr2 Null&lt;br /&gt;
                   Adr2 Datalogger_logcount&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S38:&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S34 : Adr2 S78&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
S40:&lt;br /&gt;
        Adr2 S30 : Adr2 S50 : Adr2 Null : Adr2 S41&lt;br /&gt;
        Adr2 Null&lt;br /&gt;
        Data &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
S41:&lt;br /&gt;
           Adr2 Null : Adr2 S42 : Adr2 S40 : Adr2 S45&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
S42:&lt;br /&gt;
           Adr2 S41 : Adr2 S43 : Adr2 S40 : Adr2 S46&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
S43:&lt;br /&gt;
           Adr2 S42 : Adr2 S44 : Adr2 S40 : Adr2 S47&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
S44:&lt;br /&gt;
           Adr2 S43 : Adr2 Null : Adr2 S40 : Adr2 S48&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
S45:                                                        '+24°C&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S41 : Adr2 Null&lt;br /&gt;
                   Adr2 Temperaturefunc&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S46:                                                        '0 mV&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S42 : Adr2 Null&lt;br /&gt;
                   Adr2 Voltagefunc&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S47:                                                        'CH:RAW&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S43 : Adr2 Null&lt;br /&gt;
                   Adr2 Adc_raw_func&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S48:                                                        '2900mV&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S44 : Adr2 Null&lt;br /&gt;
                   Adr2 Adc_batt_func&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S50:&lt;br /&gt;
        Adr2 S40 : Adr2 Null : Adr2 Null : Adr2 S51&lt;br /&gt;
        Adr2 Null&lt;br /&gt;
        Data &amp;quot;5 Options&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S51:&lt;br /&gt;
           Adr2 Null : Adr2 S52 : Adr2 S50 : Adr2 S56&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
S52:&lt;br /&gt;
           Adr2 S51 : Adr2 S53 : Adr2 S50 : Adr2 S57&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S53:&lt;br /&gt;
           Adr2 S52 : Adr2 S54 : Adr2 S50 : Adr2 S83&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S54:&lt;br /&gt;
           Adr2 S53 : Adr2 S55 : Adr2 S50 : Adr2 S58&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S55:&lt;br /&gt;
           Adr2 S54 : Adr2 Null : Adr2 S50 : Adr2 S59&lt;br /&gt;
           Adr2 Null&lt;br /&gt;
           Data &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
S56:                                                        '0...15&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S51 : Adr2 Null&lt;br /&gt;
                   Adr2 Setcontrast&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S57:&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S52 : Adr2 S87&lt;br /&gt;
                   Adr2 Null&lt;br /&gt;
                   Data &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S58:                                                        'OFF/5..90min&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S54 : Adr2 Null&lt;br /&gt;
                   Adr2 Autopower&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S59:                                                        'ON/OFF&lt;br /&gt;
                   Adr2 Null : Adr2 Null : Adr2 S55 : Adr2 Null&lt;br /&gt;
                   Adr2 Keyclick_set&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenüs zu State 25 bis 28&lt;br /&gt;
S65:                                                        'sub for &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
                          Adr2 S67 : Adr2 Null : Adr2 S23 : Adr2 Null&lt;br /&gt;
                          Adr2 Setclock&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S66:                                                        'sub for &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
                          Adr2 Null : Adr2 S68 : Adr2 S24 : Adr2 Null&lt;br /&gt;
                          Adr2 Setdate&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S67:                                                        'sub for &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
                          Adr2 Null : Adr2 S65 : Adr2 S23 : Adr2 Null&lt;br /&gt;
                          Adr2 Setclockformat&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S68:                                                        'sub for  &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
                          Adr2 S66 : Adr2 Null : Adr2 S24 : Adr2 Null&lt;br /&gt;
                          Adr2 Setdateformat&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenüs zu State 36 und 38&lt;br /&gt;
S76:                                                        'sub for  &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
                          Adr2 Null : Adr2 Null : Adr2 S36 : Adr2 Null&lt;br /&gt;
                          Adr2 Datalogger_erase&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S78:                                                        'sub for  &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
                          Adr2 Null : Adr2 Null : Adr2 S38 : Adr2 Null&lt;br /&gt;
                          Adr2 Datalogger_rs232&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenü zu State 53 und 57&lt;br /&gt;
S83:                                                        'sub for  &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
                          Adr2 Null : Adr2 Null : Adr2 S53 : Adr2 Null&lt;br /&gt;
                          Adr2 Power_off_func&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S87:                                                        'sub for &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
                          Adr2 Null : Adr2 Null : Adr2 S57 : Adr2 Null&lt;br /&gt;
                          Adr2 Bootfunc&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
    State_renew = 0&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = Loadlabel(s32)                                    'St_datalogger_erase&lt;br /&gt;
  Gosub Change_state_by_sub&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = Loadlabel(s34)                                   'St_datalogger_rs232 as next status of state machine&lt;br /&gt;
   Gosub Change_state_by_sub&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = Loadlabel(s10)                                   'St_avrbf as next status of state machine&lt;br /&gt;
   Gosub Change_state_by_sub&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = Loadlabel(s53)                                    'Return  State is St_options_power_off&lt;br /&gt;
  Gosub Change_state_by_sub&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]  mit Hinweisen zu einer [[Sourcevergleich#Bascom (State Machine)|State Machine in BASCOM]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
* AVR Butterfly Application code port to avr-gcc [[http://www.siwawi.arubi.uni-kl.de/avr_projects/#bf_app]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13747</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13747"/>
				<updated>2008-07-27T11:39:44Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: ADR ab Bascom Version 1.11.8.9&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly [[http://www.mikrocontroller.net/articles/AVR_Butterfly]] (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesamten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
Als Beispiel wird hier die um die Umsetzung '''des Menüs aus dem AVR Butterfly Evaluation Kit in Bascom''' gezeigt:&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 1: Umsetzung des Codes aus dem AVR Butterfly Evaluation Kit ==&lt;br /&gt;
Der Code ist die 1:1 Umsetzung des C-Codes aus dem AVR Butterfly Evaluation Kit.&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
* BASCOM 1.11.8.7 [Probleme mir der LCD Darstellung]&lt;br /&gt;
* BASCOM 1.11.8.1 [OK]&lt;br /&gt;
&lt;br /&gt;
Die Codelänge des Samples beträgt ca. 2300Byte. Der größte Teil geht für die LCD-Anzeige, Anzeigetexte etc. drauf. Der State Machine selber ist sehr schlank, da nur Pointer (Zeiger) gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 2: übersichtlich mit ADR2 ==&lt;br /&gt;
Die Variante 1 ist leider unübersichtlich, da 3 Tabellen getrennt geführt werden (die State Machine, die Menütexte und die zugehörigen Unterprogrammeinsprünge).&lt;br /&gt;
Nachfolgend eine Variante, in der nur eine Tabelle geführt wird. Die Lösung wurde möglich, nachdem in Bascom in der Version 1.11.8.9 den Befehl ADR / ADR2 eingeführt wurde.&lt;br /&gt;
&lt;br /&gt;
In jedem Datensatz stehen jetzt die Daten für die Verzweigung der State Machine (inf. Tastatureingabe), der Pointer für den Unterprogrammeinsprung und der Menütext zusammen.&lt;br /&gt;
Die Codelänge das Beispiel liegt gleichauf mit der Variante 1. Dafür ist das Menü deutlich einfacher zu editieren.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
Die Bedienung erfolgt wie bei Variante 1.&lt;br /&gt;
&lt;br /&gt;
Da jetzt zur Vereinfachung Nummern für die States geführt werden, empfiehlt es sich ein Plan (siehe Grafik oben) anzulegen und jeden Kasten mit einer Nummer zu versehen. Die Zuordnung der Status-Nummern entsprechend der Tasten-Verzweigung (UP/DOWN etc.) ist dann schnell abgelesen. Alternativ kann man natürlich beliebige Labelnamen verwenden. &lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.9.1&lt;br /&gt;
'  Codelänge 2242 Byte&lt;br /&gt;
&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in dem blauen &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code:&lt;br /&gt;
&lt;br /&gt;
Dim Keycode_string As String * 4&lt;br /&gt;
'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
Keycode_string = &amp;quot;{056}{050}{052}{054}&amp;quot;&lt;br /&gt;
Dim Keycode(4) As Byte At Keycode_string Overlay&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcd = 16 * 1&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim W As Word&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim State As Word                                           'aktueller State&lt;br /&gt;
Dim State_renew As Byte                                     'Flag&lt;br /&gt;
Dim State_gosub As Word                                     'aktuelles Unterprogramm&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State_renew = 1&lt;br /&gt;
State = Loadlabel(s10)                                      'Startbildschirm&lt;br /&gt;
Key = Key_null                                              'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
    'Menüeintrag und Tastencodes finden und ggf. State wechseln&lt;br /&gt;
    'hier nur Tastendruck auswerten&lt;br /&gt;
    Gosub Change_state&lt;br /&gt;
    'Pointer nach Statuswechsel neu setzen&lt;br /&gt;
    If State_renew = 1 Then Gosub Change_state&lt;br /&gt;
    'Unterprogramm des aktuellen State anspringen&lt;br /&gt;
    LDS R31, {State_gosub+1}                                'High see Bascom-Doc Mixing ASM and BASIC&lt;br /&gt;
    LDS R30, {State_gosub}                                  'Low&lt;br /&gt;
    LSR R31                                                 'Division durch 2 (Alternativ ADR verwenden)&lt;br /&gt;
    ROR R30&lt;br /&gt;
    'Call zum Sub&lt;br /&gt;
    ICALL&lt;br /&gt;
&lt;br /&gt;
    'Displaytext auslesen&lt;br /&gt;
    Read Lcd_textbuffer&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Tastaturabfrage mit Halt nur für Simulator, ansonsten Sleep+Timer_ISR verwenden!!&lt;br /&gt;
    Key = Waitkey()&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* State routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Change_state&lt;br /&gt;
'Call from:  main loop&lt;br /&gt;
'Purpose:    Status der State Machine feststellen und ggf. Wechseln&lt;br /&gt;
'Result:     Pointer auf State / Variable State_renew&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Change_state:&lt;br /&gt;
    lds R8, {State}&lt;br /&gt;
    lds R9, {State + 1}&lt;br /&gt;
    For I = 1 To 4&lt;br /&gt;
      Read W&lt;br /&gt;
      If Key = Keycode(i) Then&lt;br /&gt;
           If W &amp;lt;&amp;gt; Loadlabel(null) Then&lt;br /&gt;
              State_renew = 1&lt;br /&gt;
              Key = Key_null                                'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
              State = W&lt;br /&gt;
           End If&lt;br /&gt;
      End If&lt;br /&gt;
    Next I&lt;br /&gt;
    Read State_gosub                                        'Adesse des akt. Unterprogramms einlesen&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'DATA:       State Machine&lt;br /&gt;
'Result:     Pointers auf States , Unterprogramme&lt;br /&gt;
'            Menütexte&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Null:&lt;br /&gt;
         'Null is a dummy flag for State and Gosub -&amp;gt; do nothing&lt;br /&gt;
         Return&lt;br /&gt;
&lt;br /&gt;
S10:&lt;br /&gt;
         ADR2 Null : ADR2 S20 : ADR2 Null : ADR2 S11            'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
         ADR2 Null                                           'Subroutine for current State&lt;br /&gt;
         Data &amp;quot;1 Butterfly Bascom&amp;quot;                          'Menue Display Text&lt;br /&gt;
S11:&lt;br /&gt;
             ADR2 Null : ADR2 Null : ADR2 S10 : ADR2 Null       'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;11 Rev 2&amp;quot;&lt;br /&gt;
S20:&lt;br /&gt;
         ADR2 S10 : ADR2 S30 : ADR2 Null : ADR2 S21&lt;br /&gt;
         ADR2 Null&lt;br /&gt;
         Data &amp;quot;2 Time&amp;quot;&lt;br /&gt;
S21:&lt;br /&gt;
             ADR2 Null : ADR2 S22 : ADR2 S20 : ADR2 S23&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
S22:&lt;br /&gt;
             ADR2 S21 : ADR2 Null : ADR2 S20 : ADR2 S24&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;22 Date&amp;quot;&lt;br /&gt;
S23:                                                        'Show HH:MM:SS&lt;br /&gt;
                   ADR2 Null : ADR2 S24 : ADR2 S21 : ADR2 S25&lt;br /&gt;
                   ADR2 Showclock&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S24:                                                        'Show DD:MM:YY&lt;br /&gt;
                   ADR2 S23 : ADR2 Null : ADR2 S22 : ADR2 S26&lt;br /&gt;
                   ADR2 Showdate&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S25:&lt;br /&gt;
                   ADR2 S27 : ADR2 Null : ADR2 S23 : ADR2 S65&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
S26:&lt;br /&gt;
                   ADR2 Null : ADR2 S28 : ADR2 S24 : ADR2 S66&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
S27:&lt;br /&gt;
                   ADR2 Null : ADR2 S25 : ADR2 S23 : ADR2 S67&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
S28:&lt;br /&gt;
                   ADR2 S26 : ADR2 Null : ADR2 S23 : ADR2 S68&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
S30:&lt;br /&gt;
         ADR2 S20 : ADR2 S40 : ADR2 Null : ADR2 S31&lt;br /&gt;
         ADR2 Null&lt;br /&gt;
         Data &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
S31:&lt;br /&gt;
             ADR2 Null : ADR2 S32 : ADR2 S30 : ADR2 S35&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
S32:&lt;br /&gt;
             ADR2 S31 : ADR2 S33 : ADR2 S30 : ADR2 S36&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
S33:&lt;br /&gt;
             ADR2 S32 : ADR2 S34 : ADR2 S30 : ADR2 S37&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
S34:&lt;br /&gt;
             ADR2 S33 : ADR2 Null : ADR2 S30 : ADR2 S38&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
S35:                                                        'HH:MM&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S31 : ADR2 Null&lt;br /&gt;
                   ADR2 Datalogger_setloginterval&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S36:&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S32 : ADR2 S76&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
S37:                                                        '1234&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S33 : ADR2 Null&lt;br /&gt;
                   ADR2 Datalogger_logcount&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S38:&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S34 : ADR2 S78&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
S40:&lt;br /&gt;
        ADR2 S30 : ADR2 S50 : ADR2 Null : ADR2 S41&lt;br /&gt;
        ADR2 Null&lt;br /&gt;
        Data &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
S41:&lt;br /&gt;
           ADR2 Null : ADR2 S42 : ADR2 S40 : ADR2 S45&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
S42:&lt;br /&gt;
           ADR2 S41 : ADR2 S43 : ADR2 S40 : ADR2 S46&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
S43:&lt;br /&gt;
           ADR2 S42 : ADR2 S44 : ADR2 S40 : ADR2 S47&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
S44:&lt;br /&gt;
           ADR2 S43 : ADR2 Null : ADR2 S40 : ADR2 S48&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
S45:                                                        '+24°C&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S41 : ADR2 Null&lt;br /&gt;
                   ADR2 Temperaturefunc&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S46:                                                        '0 mV&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S42 : ADR2 Null&lt;br /&gt;
                   ADR2 Voltagefunc&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S47:                                                        'CH:RAW&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S43 : ADR2 Null&lt;br /&gt;
                   ADR2 Adc_raw_func&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S48:                                                        '2900mV&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S44 : ADR2 Null&lt;br /&gt;
                   ADR2 Adc_batt_func&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S50:&lt;br /&gt;
        ADR2 S40 : ADR2 Null : ADR2 Null : ADR2 S51&lt;br /&gt;
        ADR2 Null&lt;br /&gt;
        Data &amp;quot;5 Options&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S51:&lt;br /&gt;
           ADR2 Null : ADR2 S52 : ADR2 S50 : ADR2 S56&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
S52:&lt;br /&gt;
           ADR2 S51 : ADR2 S53 : ADR2 S50 : ADR2 S57&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S53:&lt;br /&gt;
           ADR2 S52 : ADR2 S54 : ADR2 S50 : ADR2 S83&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S54:&lt;br /&gt;
           ADR2 S53 : ADR2 S55 : ADR2 S50 : ADR2 S58&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S55:&lt;br /&gt;
           ADR2 S54 : ADR2 Null : ADR2 S50 : ADR2 S59&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
S56:                                                        '0...15&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S51 : ADR2 Null&lt;br /&gt;
                   ADR2 Setcontrast&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S57:&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S52 : ADR2 S87&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S58:                                                        'OFF/5..90min&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S54 : ADR2 Null&lt;br /&gt;
                   ADR2 Autopower&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S59:                                                        'ON/OFF&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S55 : ADR2 Null&lt;br /&gt;
                   ADR2 Keyclick_set&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenüs zu State 25 bis 28&lt;br /&gt;
S65:                                                        'sub for &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
                          ADR2 S67 : ADR2 Null : ADR2 S23 : ADR2 Null&lt;br /&gt;
                          ADR2 Setclock&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S66:                                                        'sub for &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 S68 : ADR2 S24 : ADR2 Null&lt;br /&gt;
                          ADR2 Setdate&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S67:                                                        'sub for &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 S65 : ADR2 S23 : ADR2 Null&lt;br /&gt;
                          ADR2 Setclockformat&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S68:                                                        'sub for  &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
                          ADR2 S66 : ADR2 Null : ADR2 S24 : ADR2 Null&lt;br /&gt;
                          ADR2 Setdateformat&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenüs zu State 36 und 38&lt;br /&gt;
S76:                                                        'sub for  &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S36 : ADR2 Null&lt;br /&gt;
                          ADR2 Datalogger_erase&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S78:                                                        'sub for  &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S38 : ADR2 Null&lt;br /&gt;
                          ADR2 Datalogger_rs232&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenü zu State 53 und 57&lt;br /&gt;
S83:                                                        'sub for  &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S53 : ADR2 Null&lt;br /&gt;
                          ADR2 Power_off_func&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S87:                                                        'sub for &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S57 : ADR2 Null&lt;br /&gt;
                          ADR2 Bootfunc&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
    State_renew = 0&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = Loadlabel(s32)                                    'St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
  Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = Loadlabel(s34)                                   'St_datalogger_rs232 as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
   Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = Loadlabel(s10)                                   'St_avrbf as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
   Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = Loadlabel(s53)                                    'Return  State is St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]  mit Hinweisen zu einer [[Sourcevergleich#Bascom (State Machine)|State Machine in BASCOM]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
* AVR Butterfly Application code port to avr-gcc [[http://www.siwawi.arubi.uni-kl.de/avr_projects/#bf_app]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13741</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13741"/>
				<updated>2008-07-26T10:55:13Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly [[http://www.mikrocontroller.net/articles/AVR_Butterfly]] (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesamten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
Als Beispiel wird hier die um die Umsetzung '''des Menüs aus dem AVR Butterfly Evaluation Kit in Bascom''' gezeigt:&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 1: Umsetzung des Codes aus dem AVR Butterfly Evaluation Kit ==&lt;br /&gt;
Der Code ist die 1:1 Umsetzung des C-Codes aus dem AVR Butterfly Evaluation Kit.&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
* BASCOM 1.11.8.7 [Probleme mir der LCD Darstellung]&lt;br /&gt;
* BASCOM 1.11.8.1 [OK]&lt;br /&gt;
&lt;br /&gt;
Die Codelänge des Samples beträgt ca. 2300Byte. Der größte Teil geht für die LCD-Anzeige, Anzeigetexte etc. drauf. Der State Machine selber ist sehr schlank, da nur Pointer (Zeiger) gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 2: übersichtlich mit ADR2 ==&lt;br /&gt;
Die Variante 1 ist leider unübersichtlich, da 3 Tabellen getrennt geführt werden (die State Machine, die Menütexte und die zugehörigen Unterprogrammeinsprünge).&lt;br /&gt;
Nachfolgend eine Variante, in der nur eine Tabelle geführt wird. Die Lösung wurde möglich, nachdem in Bascom in der Version 1.8.xx den Befehl ADR / ADR2 eingeführt wurde.&lt;br /&gt;
&lt;br /&gt;
In jedem Datensatz stehen jetzt die Daten für die Verzweigung der State Machine (inf. Tastatureingabe), der Pointer für den Unterprogrammeinsprung und der Menütext zusammen.&lt;br /&gt;
Die Codelänge das Beispiel liegt gleichauf mit der Variante 1. Dafür ist das Menü deutlich einfacher zu editieren.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
Die Bedienung erfolgt wie bei Variante 1.&lt;br /&gt;
&lt;br /&gt;
Da jetzt zur Vereinfachung Nummern für die States geführt werden, empfiehlt es sich ein Plan (siehe Grafik oben) anzulegen und jeden Kasten mit einer Nummer zu versehen. Die Zuordnung der Status-Nummern entsprechend der Tasten-Verzweigung (UP/DOWN etc.) ist dann schnell abgelesen. Alternativ kann man natürlich beliebige Labelnamen verwenden. &lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.9.1&lt;br /&gt;
'  Codelänge 2242 Byte&lt;br /&gt;
&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in dem blauen &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code:&lt;br /&gt;
&lt;br /&gt;
Dim Keycode_string As String * 4&lt;br /&gt;
'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
Keycode_string = &amp;quot;{056}{050}{052}{054}&amp;quot;&lt;br /&gt;
Dim Keycode(4) As Byte At Keycode_string Overlay&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcd = 16 * 1&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim W As Word&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim State As Word                                           'aktueller State&lt;br /&gt;
Dim State_renew As Byte                                     'Flag&lt;br /&gt;
Dim State_gosub As Word                                     'aktuelles Unterprogramm&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State_renew = 1&lt;br /&gt;
State = Loadlabel(s10)                                      'Startbildschirm&lt;br /&gt;
Key = Key_null                                              'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
    'Menüeintrag und Tastencodes finden und ggf. State wechseln&lt;br /&gt;
    'hier nur Tastendruck auswerten&lt;br /&gt;
    Gosub Change_state&lt;br /&gt;
    'Pointer nach Statuswechsel neu setzen&lt;br /&gt;
    If State_renew = 1 Then Gosub Change_state&lt;br /&gt;
    'Unterprogramm des aktuellen State anspringen&lt;br /&gt;
    LDS R31, {State_gosub+1}                                'High see Bascom-Doc Mixing ASM and BASIC&lt;br /&gt;
    LDS R30, {State_gosub}                                  'Low&lt;br /&gt;
    LSR R31                                                 'Division durch 2 (Alternativ ADR verwenden)&lt;br /&gt;
    ROR R30&lt;br /&gt;
    'Call zum Sub&lt;br /&gt;
    ICALL&lt;br /&gt;
&lt;br /&gt;
    'Displaytext auslesen&lt;br /&gt;
    Read Lcd_textbuffer&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Tastaturabfrage mit Halt nur für Simulator, ansonsten Sleep+Timer_ISR verwenden!!&lt;br /&gt;
    Key = Waitkey()&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* State routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Change_state&lt;br /&gt;
'Call from:  main loop&lt;br /&gt;
'Purpose:    Status der State Machine feststellen und ggf. Wechseln&lt;br /&gt;
'Result:     Pointer auf State / Variable State_renew&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Change_state:&lt;br /&gt;
    lds R8, {State}&lt;br /&gt;
    lds R9, {State + 1}&lt;br /&gt;
    For I = 1 To 4&lt;br /&gt;
      Read W&lt;br /&gt;
      If Key = Keycode(i) Then&lt;br /&gt;
           If W &amp;lt;&amp;gt; Loadlabel(null) Then&lt;br /&gt;
              State_renew = 1&lt;br /&gt;
              Key = Key_null                                'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
              State = W&lt;br /&gt;
           End If&lt;br /&gt;
      End If&lt;br /&gt;
    Next I&lt;br /&gt;
    Read State_gosub                                        'Adesse des akt. Unterprogramms einlesen&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'DATA:       State Machine&lt;br /&gt;
'Result:     Pointers auf States , Unterprogramme&lt;br /&gt;
'            Menütexte&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Null:&lt;br /&gt;
         'Null is a dummy flag for State and Gosub -&amp;gt; do nothing&lt;br /&gt;
         Return&lt;br /&gt;
&lt;br /&gt;
S10:&lt;br /&gt;
         ADR2 Null : ADR2 S20 : ADR2 Null : ADR2 S11            'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
         ADR2 Null                                           'Subroutine for current State&lt;br /&gt;
         Data &amp;quot;1 Butterfly Bascom&amp;quot;                          'Menue Display Text&lt;br /&gt;
S11:&lt;br /&gt;
             ADR2 Null : ADR2 Null : ADR2 S10 : ADR2 Null       'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;11 Rev 2&amp;quot;&lt;br /&gt;
S20:&lt;br /&gt;
         ADR2 S10 : ADR2 S30 : ADR2 Null : ADR2 S21&lt;br /&gt;
         ADR2 Null&lt;br /&gt;
         Data &amp;quot;2 Time&amp;quot;&lt;br /&gt;
S21:&lt;br /&gt;
             ADR2 Null : ADR2 S22 : ADR2 S20 : ADR2 S23&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
S22:&lt;br /&gt;
             ADR2 S21 : ADR2 Null : ADR2 S20 : ADR2 S24&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;22 Date&amp;quot;&lt;br /&gt;
S23:                                                        'Show HH:MM:SS&lt;br /&gt;
                   ADR2 Null : ADR2 S24 : ADR2 S21 : ADR2 S25&lt;br /&gt;
                   ADR2 Showclock&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S24:                                                        'Show DD:MM:YY&lt;br /&gt;
                   ADR2 S23 : ADR2 Null : ADR2 S22 : ADR2 S26&lt;br /&gt;
                   ADR2 Showdate&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S25:&lt;br /&gt;
                   ADR2 S27 : ADR2 Null : ADR2 S23 : ADR2 S65&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
S26:&lt;br /&gt;
                   ADR2 Null : ADR2 S28 : ADR2 S24 : ADR2 S66&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
S27:&lt;br /&gt;
                   ADR2 Null : ADR2 S25 : ADR2 S23 : ADR2 S67&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
S28:&lt;br /&gt;
                   ADR2 S26 : ADR2 Null : ADR2 S23 : ADR2 S68&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
S30:&lt;br /&gt;
         ADR2 S20 : ADR2 S40 : ADR2 Null : ADR2 S31&lt;br /&gt;
         ADR2 Null&lt;br /&gt;
         Data &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
S31:&lt;br /&gt;
             ADR2 Null : ADR2 S32 : ADR2 S30 : ADR2 S35&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
S32:&lt;br /&gt;
             ADR2 S31 : ADR2 S33 : ADR2 S30 : ADR2 S36&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
S33:&lt;br /&gt;
             ADR2 S32 : ADR2 S34 : ADR2 S30 : ADR2 S37&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
S34:&lt;br /&gt;
             ADR2 S33 : ADR2 Null : ADR2 S30 : ADR2 S38&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
S35:                                                        'HH:MM&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S31 : ADR2 Null&lt;br /&gt;
                   ADR2 Datalogger_setloginterval&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S36:&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S32 : ADR2 S76&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
S37:                                                        '1234&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S33 : ADR2 Null&lt;br /&gt;
                   ADR2 Datalogger_logcount&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S38:&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S34 : ADR2 S78&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
S40:&lt;br /&gt;
        ADR2 S30 : ADR2 S50 : ADR2 Null : ADR2 S41&lt;br /&gt;
        ADR2 Null&lt;br /&gt;
        Data &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
S41:&lt;br /&gt;
           ADR2 Null : ADR2 S42 : ADR2 S40 : ADR2 S45&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
S42:&lt;br /&gt;
           ADR2 S41 : ADR2 S43 : ADR2 S40 : ADR2 S46&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
S43:&lt;br /&gt;
           ADR2 S42 : ADR2 S44 : ADR2 S40 : ADR2 S47&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
S44:&lt;br /&gt;
           ADR2 S43 : ADR2 Null : ADR2 S40 : ADR2 S48&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
S45:                                                        '+24°C&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S41 : ADR2 Null&lt;br /&gt;
                   ADR2 Temperaturefunc&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S46:                                                        '0 mV&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S42 : ADR2 Null&lt;br /&gt;
                   ADR2 Voltagefunc&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S47:                                                        'CH:RAW&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S43 : ADR2 Null&lt;br /&gt;
                   ADR2 Adc_raw_func&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S48:                                                        '2900mV&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S44 : ADR2 Null&lt;br /&gt;
                   ADR2 Adc_batt_func&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S50:&lt;br /&gt;
        ADR2 S40 : ADR2 Null : ADR2 Null : ADR2 S51&lt;br /&gt;
        ADR2 Null&lt;br /&gt;
        Data &amp;quot;5 Options&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S51:&lt;br /&gt;
           ADR2 Null : ADR2 S52 : ADR2 S50 : ADR2 S56&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
S52:&lt;br /&gt;
           ADR2 S51 : ADR2 S53 : ADR2 S50 : ADR2 S57&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S53:&lt;br /&gt;
           ADR2 S52 : ADR2 S54 : ADR2 S50 : ADR2 S83&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S54:&lt;br /&gt;
           ADR2 S53 : ADR2 S55 : ADR2 S50 : ADR2 S58&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S55:&lt;br /&gt;
           ADR2 S54 : ADR2 Null : ADR2 S50 : ADR2 S59&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
S56:                                                        '0...15&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S51 : ADR2 Null&lt;br /&gt;
                   ADR2 Setcontrast&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S57:&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S52 : ADR2 S87&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S58:                                                        'OFF/5..90min&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S54 : ADR2 Null&lt;br /&gt;
                   ADR2 Autopower&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S59:                                                        'ON/OFF&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S55 : ADR2 Null&lt;br /&gt;
                   ADR2 Keyclick_set&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenüs zu State 25 bis 28&lt;br /&gt;
S65:                                                        'sub for &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
                          ADR2 S67 : ADR2 Null : ADR2 S23 : ADR2 Null&lt;br /&gt;
                          ADR2 Setclock&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S66:                                                        'sub for &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 S68 : ADR2 S24 : ADR2 Null&lt;br /&gt;
                          ADR2 Setdate&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S67:                                                        'sub for &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 S65 : ADR2 S23 : ADR2 Null&lt;br /&gt;
                          ADR2 Setclockformat&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S68:                                                        'sub for  &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
                          ADR2 S66 : ADR2 Null : ADR2 S24 : ADR2 Null&lt;br /&gt;
                          ADR2 Setdateformat&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenüs zu State 36 und 38&lt;br /&gt;
S76:                                                        'sub for  &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S36 : ADR2 Null&lt;br /&gt;
                          ADR2 Datalogger_erase&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S78:                                                        'sub for  &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S38 : ADR2 Null&lt;br /&gt;
                          ADR2 Datalogger_rs232&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenü zu State 53 und 57&lt;br /&gt;
S83:                                                        'sub for  &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S53 : ADR2 Null&lt;br /&gt;
                          ADR2 Power_off_func&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S87:                                                        'sub for &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S57 : ADR2 Null&lt;br /&gt;
                          ADR2 Bootfunc&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
    State_renew = 0&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = Loadlabel(s32)                                    'St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
  Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = Loadlabel(s34)                                   'St_datalogger_rs232 as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
   Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = Loadlabel(s10)                                   'St_avrbf as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
   Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = Loadlabel(s53)                                    'Return  State is St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]  mit Hinweisen zu einer [[Sourcevergleich#Bascom (State Machine)|State Machine in BASCOM]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
* AVR Butterfly Application code port to avr-gcc [[http://www.siwawi.arubi.uni-kl.de/avr_projects/#bf_app]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13740</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13740"/>
				<updated>2008-07-26T10:50:20Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Sample Code Variante 1: unübersichtlich und wenig Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly [[http://www.mikrocontroller.net/articles/AVR_Butterfly]] (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesamten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
Als Beispiel wird hier die um die Umsetzung '''des Menüs aus dem AVR Butterfly Evaluation Kit in Bascom''' gezeigt:&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 1: Umsetzung des Codes aus dem AVR Butterfly Evaluation Kit ==&lt;br /&gt;
Der Code ist die 1:1 Umsetzung des C-Codes aus dem AVR Butterfly Evaluation Kit.&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
* BASCOM 1.11.8.7 [Probleme mir der LCD Darstellung]&lt;br /&gt;
* BASCOM 1.11.8.1 [OK]&lt;br /&gt;
&lt;br /&gt;
Die Codelänge des Samples beträgt ca. 2300Byte. Der größte Teil geht für die LCD-Anzeige, Anzeigetexte etc. drauf. Der State Machine selber ist sehr schlank, da nur Pointer (Zeiger) gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 2: übersichtlich mit ADR2 ==&lt;br /&gt;
Die Variante 1 ist leider unübersichtlich, da 3 Tabellen getrennt geführt werden (die State Machine, die Menütexte und die zugehörigen Unterprogrammeinsprünge).&lt;br /&gt;
Nachfolgend eine Variante, in der nur eine Tabelle geführt wird. Die Lösung wurde möglich, nachdem in Bascom in der Version 1.8.xx den Befehl ADR / ADR2 eingeführt wurde.&lt;br /&gt;
&lt;br /&gt;
In jedem Datensatz stehen jetzt die Daten für die Verzweigung der State Machine (inf. Tastatureingabe), der Pointer für den Unterprogrammeinsprung und der Menütext zusammen.&lt;br /&gt;
Die Codelänge das Beispiel liegt gleichauf mit der Variante 1. Dafür ist das Menü deutlich einfacher zu editieren.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
Die Bedienung erfolgt wie bei Variante 1.&lt;br /&gt;
&lt;br /&gt;
Da jetzt zur Vereinfachung Nummern für die States geführt werden, empfiehlt es sich ein Plan (siehe Grafik oben) anzulegen und jeden Kasten mit einer Nummer zu versehen. Die Zuordnung der Status-Nummern entsprechend der Tasten-Verzweigung (UP/DOWN etc.) ist dann schnell abgelesen. Alternativ kann man natürlich beliebige Labelnamen verwenden. &lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.9.1&lt;br /&gt;
'  Codelänge 2242 Byte&lt;br /&gt;
&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in dem blauen &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code:&lt;br /&gt;
&lt;br /&gt;
Dim Keycode_string As String * 4&lt;br /&gt;
'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
Keycode_string = &amp;quot;{056}{050}{052}{054}&amp;quot;&lt;br /&gt;
Dim Keycode(4) As Byte At Keycode_string Overlay&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcd = 16 * 1&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim W As Word&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim State As Word                                           'aktueller State&lt;br /&gt;
Dim State_renew As Byte                                     'Flag&lt;br /&gt;
Dim State_gosub As Word                                     'aktuelles Unterprogramm&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State_renew = 1&lt;br /&gt;
State = Loadlabel(s10)                                      'Startbildschirm&lt;br /&gt;
Key = Key_null                                              'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
    'Menüeintrag und Tastencodes finden und ggf. State wechseln&lt;br /&gt;
    'hier nur Tastendruck auswerten&lt;br /&gt;
    Gosub Change_state&lt;br /&gt;
    'Pointer nach Statuswechsel neu setzen&lt;br /&gt;
    If State_renew = 1 Then Gosub Change_state&lt;br /&gt;
    'Unterprogramm des aktuellen State anspringen&lt;br /&gt;
    LDS R31, {State_gosub+1}                                'High see Bascom-Doc Mixing ASM and BASIC&lt;br /&gt;
    LDS R30, {State_gosub}                                  'Low&lt;br /&gt;
    LSR R31                                                 'Division durch 2 (Alternativ ADR verwenden)&lt;br /&gt;
    ROR R30&lt;br /&gt;
    'Call zum Sub&lt;br /&gt;
    ICALL&lt;br /&gt;
&lt;br /&gt;
    'Displaytext auslesen&lt;br /&gt;
    Read Lcd_textbuffer&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Tastaturabfrage mit Halt nur für Simulator, ansonsten Sleep+Timer_ISR verwenden!!&lt;br /&gt;
    Key = Waitkey()&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* State routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Change_state&lt;br /&gt;
'Call from:  main loop&lt;br /&gt;
'Purpose:    Status der State Machine feststellen und ggf. Wechseln&lt;br /&gt;
'Result:     Pointer auf State / Variable State_renew&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Change_state:&lt;br /&gt;
    'W = State * 2&lt;br /&gt;
    lds R8, {State}&lt;br /&gt;
    lds R9, {State + 1}&lt;br /&gt;
    For I = 1 To 4&lt;br /&gt;
      Read W&lt;br /&gt;
      If Key = Keycode(i) Then&lt;br /&gt;
           If W &amp;lt;&amp;gt; Loadlabel(null) Then&lt;br /&gt;
              State_renew = 1&lt;br /&gt;
              Key = Key_null                                'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
              State = W&lt;br /&gt;
           End If&lt;br /&gt;
      End If&lt;br /&gt;
    Next I&lt;br /&gt;
    Read State_gosub                                        'Adesse des akt. Unterprogramms einlesen&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'DATA:       State Machine&lt;br /&gt;
'Result:     Pointers auf States , Unterprogramme&lt;br /&gt;
'            Menütexte&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Null:&lt;br /&gt;
         'Null is a dummy flag for State and Gosub -&amp;gt; do nothing&lt;br /&gt;
         Return&lt;br /&gt;
&lt;br /&gt;
S10:&lt;br /&gt;
         ADR2 Null : ADR2 S20 : ADR2 Null : ADR2 S11            'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
         ADR2 Null                                           'Subroutine for current State&lt;br /&gt;
         Data &amp;quot;1 Butterfly Bascom&amp;quot;                          'Menue Display Text&lt;br /&gt;
S11:&lt;br /&gt;
             ADR2 Null : ADR2 Null : ADR2 S10 : ADR2 Null       'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;11 Rev 2&amp;quot;&lt;br /&gt;
S20:&lt;br /&gt;
         ADR2 S10 : ADR2 S30 : ADR2 Null : ADR2 S21&lt;br /&gt;
         ADR2 Null&lt;br /&gt;
         Data &amp;quot;2 Time&amp;quot;&lt;br /&gt;
S21:&lt;br /&gt;
             ADR2 Null : ADR2 S22 : ADR2 S20 : ADR2 S23&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
S22:&lt;br /&gt;
             ADR2 S21 : ADR2 Null : ADR2 S20 : ADR2 S24&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;22 Date&amp;quot;&lt;br /&gt;
S23:                                                        'Show HH:MM:SS&lt;br /&gt;
                   ADR2 Null : ADR2 S24 : ADR2 S21 : ADR2 S25&lt;br /&gt;
                   ADR2 Showclock&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S24:                                                        'Show DD:MM:YY&lt;br /&gt;
                   ADR2 S23 : ADR2 Null : ADR2 S22 : ADR2 S26&lt;br /&gt;
                   ADR2 Showdate&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S25:&lt;br /&gt;
                   ADR2 S27 : ADR2 Null : ADR2 S23 : ADR2 S65&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
S26:&lt;br /&gt;
                   ADR2 Null : ADR2 S28 : ADR2 S24 : ADR2 S66&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
S27:&lt;br /&gt;
                   ADR2 Null : ADR2 S25 : ADR2 S23 : ADR2 S67&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
S28:&lt;br /&gt;
                   ADR2 S26 : ADR2 Null : ADR2 S23 : ADR2 S68&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
S30:&lt;br /&gt;
         ADR2 S20 : ADR2 S40 : ADR2 Null : ADR2 S31&lt;br /&gt;
         ADR2 Null&lt;br /&gt;
         Data &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
S31:&lt;br /&gt;
             ADR2 Null : ADR2 S32 : ADR2 S30 : ADR2 S35&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
S32:&lt;br /&gt;
             ADR2 S31 : ADR2 S33 : ADR2 S30 : ADR2 S36&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
S33:&lt;br /&gt;
             ADR2 S32 : ADR2 S34 : ADR2 S30 : ADR2 S37&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
S34:&lt;br /&gt;
             ADR2 S33 : ADR2 Null : ADR2 S30 : ADR2 S38&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
S35:                                                        'HH:MM&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S31 : ADR2 Null&lt;br /&gt;
                   ADR2 Datalogger_setloginterval&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S36:&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S32 : ADR2 S76&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
S37:                                                        '1234&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S33 : ADR2 Null&lt;br /&gt;
                   ADR2 Datalogger_logcount&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S38:&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S34 : ADR2 S78&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
S40:&lt;br /&gt;
        ADR2 S30 : ADR2 S50 : ADR2 Null : ADR2 S41&lt;br /&gt;
        ADR2 Null&lt;br /&gt;
        Data &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
S41:&lt;br /&gt;
           ADR2 Null : ADR2 S42 : ADR2 S40 : ADR2 S45&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
S42:&lt;br /&gt;
           ADR2 S41 : ADR2 S43 : ADR2 S40 : ADR2 S46&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
S43:&lt;br /&gt;
           ADR2 S42 : ADR2 S44 : ADR2 S40 : ADR2 S47&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
S44:&lt;br /&gt;
           ADR2 S43 : ADR2 Null : ADR2 S40 : ADR2 S48&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
S45:                                                        '+24°C&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S41 : ADR2 Null&lt;br /&gt;
                   ADR2 Temperaturefunc&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S46:                                                        '0 mV&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S42 : ADR2 Null&lt;br /&gt;
                   ADR2 Voltagefunc&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S47:                                                        'CH:RAW&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S43 : ADR2 Null&lt;br /&gt;
                   ADR2 Adc_raw_func&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S48:                                                        '2900mV&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S44 : ADR2 Null&lt;br /&gt;
                   ADR2 Adc_batt_func&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S50:&lt;br /&gt;
        ADR2 S40 : ADR2 Null : ADR2 Null : ADR2 S51&lt;br /&gt;
        ADR2 Null&lt;br /&gt;
        Data &amp;quot;5 Options&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S51:&lt;br /&gt;
           ADR2 Null : ADR2 S52 : ADR2 S50 : ADR2 S56&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
S52:&lt;br /&gt;
           ADR2 S51 : ADR2 S53 : ADR2 S50 : ADR2 S57&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S53:&lt;br /&gt;
           ADR2 S52 : ADR2 S54 : ADR2 S50 : ADR2 S83&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S54:&lt;br /&gt;
           ADR2 S53 : ADR2 S55 : ADR2 S50 : ADR2 S58&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S55:&lt;br /&gt;
           ADR2 S54 : ADR2 Null : ADR2 S50 : ADR2 S59&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
S56:                                                        '0...15&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S51 : ADR2 Null&lt;br /&gt;
                   ADR2 Setcontrast&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S57:&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S52 : ADR2 S87&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S58:                                                        'OFF/5..90min&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S54 : ADR2 Null&lt;br /&gt;
                   ADR2 Autopower&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S59:                                                        'ON/OFF&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S55 : ADR2 Null&lt;br /&gt;
                   ADR2 Keyclick_set&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenüs zu State 25 bis 28&lt;br /&gt;
S65:                                                        'sub for &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
                          ADR2 S67 : ADR2 Null : ADR2 S23 : ADR2 Null&lt;br /&gt;
                          ADR2 Setclock&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S66:                                                        'sub for &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 S68 : ADR2 S24 : ADR2 Null&lt;br /&gt;
                          ADR2 Setdate&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S67:                                                        'sub for &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 S65 : ADR2 S23 : ADR2 Null&lt;br /&gt;
                          ADR2 Setclockformat&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S68:                                                        'sub for  &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
                          ADR2 S66 : ADR2 Null : ADR2 S24 : ADR2 Null&lt;br /&gt;
                          ADR2 Setdateformat&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenüs zu State 36 und 38&lt;br /&gt;
S76:                                                        'sub for  &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S36 : ADR2 Null&lt;br /&gt;
                          ADR2 Datalogger_erase&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S78:                                                        'sub for  &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S38 : ADR2 Null&lt;br /&gt;
                          ADR2 Datalogger_rs232&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenü zu State 53 und 57&lt;br /&gt;
S83:                                                        'sub for  &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S53 : ADR2 Null&lt;br /&gt;
                          ADR2 Power_off_func&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S87:                                                        'sub for &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S57 : ADR2 Null&lt;br /&gt;
                          ADR2 Bootfunc&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
    State_renew = 0&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = Loadlabel(s32)                                    'St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
  Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = Loadlabel(s34)                                   'St_datalogger_rs232 as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
   Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = Loadlabel(s10)                                   'St_avrbf as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
   Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = Loadlabel(s53)                                    'Return  State is St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]  mit Hinweisen zu einer [[Sourcevergleich#Bascom (State Machine)|State Machine in BASCOM]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
* AVR Butterfly Application code port to avr-gcc [[http://www.siwawi.arubi.uni-kl.de/avr_projects/#bf_app]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13739</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13739"/>
				<updated>2008-07-26T10:47:15Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: Variante 2 komplett geändert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly [[http://www.mikrocontroller.net/articles/AVR_Butterfly]] (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesamten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
Als Beispiel wird hier die um die Umsetzung '''des Menüs aus dem AVR Butterfly Evaluation Kit in Bascom''' gezeigt:&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 1: unübersichtlich und wenig Code ==&lt;br /&gt;
Der Code ist die 1:1 Umsetzung des C-Codes aus dem AVR Butterfly Evaluation Kit.&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
* BASCOM 1.11.8.7 [Probleme mir der LCD Darstellung]&lt;br /&gt;
* BASCOM 1.11.8.1 [OK]&lt;br /&gt;
&lt;br /&gt;
Die Codelänge des Samples beträgt ca. 2300Byte. Der größte Teil geht für die LCD-Anzeige, Anzeigetexte etc. drauf. Der State Machine selber ist sehr schlank, da nur Pointer (Zeiger) gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 2: übersichtlich mit ADR2 ==&lt;br /&gt;
Die Variante 1 ist leider unübersichtlich, da 3 Tabellen getrennt geführt werden (die State Machine, die Menütexte und die zugehörigen Unterprogrammeinsprünge).&lt;br /&gt;
Nachfolgend eine Variante, in der nur eine Tabelle geführt wird. Die Lösung wurde möglich, nachdem in Bascom in der Version 1.8.xx den Befehl ADR / ADR2 eingeführt wurde.&lt;br /&gt;
&lt;br /&gt;
In jedem Datensatz stehen jetzt die Daten für die Verzweigung der State Machine (inf. Tastatureingabe), der Pointer für den Unterprogrammeinsprung und der Menütext zusammen.&lt;br /&gt;
Die Codelänge das Beispiel liegt gleichauf mit der Variante 1. Dafür ist das Menü deutlich einfacher zu editieren.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
Die Bedienung erfolgt wie bei Variante 1.&lt;br /&gt;
&lt;br /&gt;
Da jetzt zur Vereinfachung Nummern für die States geführt werden, empfiehlt es sich ein Plan (siehe Grafik oben) anzulegen und jeden Kasten mit einer Nummer zu versehen. Die Zuordnung der Status-Nummern entsprechend der Tasten-Verzweigung (UP/DOWN etc.) ist dann schnell abgelesen. Alternativ kann man natürlich beliebige Labelnamen verwenden. &lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.9.1&lt;br /&gt;
'  Codelänge 2242 Byte&lt;br /&gt;
&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in dem blauen &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code:&lt;br /&gt;
&lt;br /&gt;
Dim Keycode_string As String * 4&lt;br /&gt;
'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
Keycode_string = &amp;quot;{056}{050}{052}{054}&amp;quot;&lt;br /&gt;
Dim Keycode(4) As Byte At Keycode_string Overlay&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcd = 16 * 1&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim W As Word&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim State As Word                                           'aktueller State&lt;br /&gt;
Dim State_renew As Byte                                     'Flag&lt;br /&gt;
Dim State_gosub As Word                                     'aktuelles Unterprogramm&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State_renew = 1&lt;br /&gt;
State = Loadlabel(s10)                                      'Startbildschirm&lt;br /&gt;
Key = Key_null                                              'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
    'Menüeintrag und Tastencodes finden und ggf. State wechseln&lt;br /&gt;
    'hier nur Tastendruck auswerten&lt;br /&gt;
    Gosub Change_state&lt;br /&gt;
    'Pointer nach Statuswechsel neu setzen&lt;br /&gt;
    If State_renew = 1 Then Gosub Change_state&lt;br /&gt;
    'Unterprogramm des aktuellen State anspringen&lt;br /&gt;
    LDS R31, {State_gosub+1}                                'High see Bascom-Doc Mixing ASM and BASIC&lt;br /&gt;
    LDS R30, {State_gosub}                                  'Low&lt;br /&gt;
    LSR R31                                                 'Division durch 2 (Alternativ ADR verwenden)&lt;br /&gt;
    ROR R30&lt;br /&gt;
    'Call zum Sub&lt;br /&gt;
    ICALL&lt;br /&gt;
&lt;br /&gt;
    'Displaytext auslesen&lt;br /&gt;
    Read Lcd_textbuffer&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Tastaturabfrage mit Halt nur für Simulator, ansonsten Sleep+Timer_ISR verwenden!!&lt;br /&gt;
    Key = Waitkey()&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* State routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Change_state&lt;br /&gt;
'Call from:  main loop&lt;br /&gt;
'Purpose:    Status der State Machine feststellen und ggf. Wechseln&lt;br /&gt;
'Result:     Pointer auf State / Variable State_renew&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Change_state:&lt;br /&gt;
    'W = State * 2&lt;br /&gt;
    lds R8, {State}&lt;br /&gt;
    lds R9, {State + 1}&lt;br /&gt;
    For I = 1 To 4&lt;br /&gt;
      Read W&lt;br /&gt;
      If Key = Keycode(i) Then&lt;br /&gt;
           If W &amp;lt;&amp;gt; Loadlabel(null) Then&lt;br /&gt;
              State_renew = 1&lt;br /&gt;
              Key = Key_null                                'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
              State = W&lt;br /&gt;
           End If&lt;br /&gt;
      End If&lt;br /&gt;
    Next I&lt;br /&gt;
    Read State_gosub                                        'Adesse des akt. Unterprogramms einlesen&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'DATA:       State Machine&lt;br /&gt;
'Result:     Pointers auf States , Unterprogramme&lt;br /&gt;
'            Menütexte&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Null:&lt;br /&gt;
         'Null is a dummy flag for State and Gosub -&amp;gt; do nothing&lt;br /&gt;
         Return&lt;br /&gt;
&lt;br /&gt;
S10:&lt;br /&gt;
         ADR2 Null : ADR2 S20 : ADR2 Null : ADR2 S11            'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
         ADR2 Null                                           'Subroutine for current State&lt;br /&gt;
         Data &amp;quot;1 Butterfly Bascom&amp;quot;                          'Menue Display Text&lt;br /&gt;
S11:&lt;br /&gt;
             ADR2 Null : ADR2 Null : ADR2 S10 : ADR2 Null       'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;11 Rev 2&amp;quot;&lt;br /&gt;
S20:&lt;br /&gt;
         ADR2 S10 : ADR2 S30 : ADR2 Null : ADR2 S21&lt;br /&gt;
         ADR2 Null&lt;br /&gt;
         Data &amp;quot;2 Time&amp;quot;&lt;br /&gt;
S21:&lt;br /&gt;
             ADR2 Null : ADR2 S22 : ADR2 S20 : ADR2 S23&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
S22:&lt;br /&gt;
             ADR2 S21 : ADR2 Null : ADR2 S20 : ADR2 S24&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;22 Date&amp;quot;&lt;br /&gt;
S23:                                                        'Show HH:MM:SS&lt;br /&gt;
                   ADR2 Null : ADR2 S24 : ADR2 S21 : ADR2 S25&lt;br /&gt;
                   ADR2 Showclock&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S24:                                                        'Show DD:MM:YY&lt;br /&gt;
                   ADR2 S23 : ADR2 Null : ADR2 S22 : ADR2 S26&lt;br /&gt;
                   ADR2 Showdate&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S25:&lt;br /&gt;
                   ADR2 S27 : ADR2 Null : ADR2 S23 : ADR2 S65&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
S26:&lt;br /&gt;
                   ADR2 Null : ADR2 S28 : ADR2 S24 : ADR2 S66&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
S27:&lt;br /&gt;
                   ADR2 Null : ADR2 S25 : ADR2 S23 : ADR2 S67&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
S28:&lt;br /&gt;
                   ADR2 S26 : ADR2 Null : ADR2 S23 : ADR2 S68&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
S30:&lt;br /&gt;
         ADR2 S20 : ADR2 S40 : ADR2 Null : ADR2 S31&lt;br /&gt;
         ADR2 Null&lt;br /&gt;
         Data &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
S31:&lt;br /&gt;
             ADR2 Null : ADR2 S32 : ADR2 S30 : ADR2 S35&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
S32:&lt;br /&gt;
             ADR2 S31 : ADR2 S33 : ADR2 S30 : ADR2 S36&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
S33:&lt;br /&gt;
             ADR2 S32 : ADR2 S34 : ADR2 S30 : ADR2 S37&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
S34:&lt;br /&gt;
             ADR2 S33 : ADR2 Null : ADR2 S30 : ADR2 S38&lt;br /&gt;
             ADR2 Null&lt;br /&gt;
             Data &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
S35:                                                        'HH:MM&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S31 : ADR2 Null&lt;br /&gt;
                   ADR2 Datalogger_setloginterval&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S36:&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S32 : ADR2 S76&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
S37:                                                        '1234&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S33 : ADR2 Null&lt;br /&gt;
                   ADR2 Datalogger_logcount&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S38:&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S34 : ADR2 S78&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
S40:&lt;br /&gt;
        ADR2 S30 : ADR2 S50 : ADR2 Null : ADR2 S41&lt;br /&gt;
        ADR2 Null&lt;br /&gt;
        Data &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
S41:&lt;br /&gt;
           ADR2 Null : ADR2 S42 : ADR2 S40 : ADR2 S45&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
S42:&lt;br /&gt;
           ADR2 S41 : ADR2 S43 : ADR2 S40 : ADR2 S46&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
S43:&lt;br /&gt;
           ADR2 S42 : ADR2 S44 : ADR2 S40 : ADR2 S47&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
S44:&lt;br /&gt;
           ADR2 S43 : ADR2 Null : ADR2 S40 : ADR2 S48&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
S45:                                                        '+24°C&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S41 : ADR2 Null&lt;br /&gt;
                   ADR2 Temperaturefunc&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S46:                                                        '0 mV&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S42 : ADR2 Null&lt;br /&gt;
                   ADR2 Voltagefunc&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S47:                                                        'CH:RAW&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S43 : ADR2 Null&lt;br /&gt;
                   ADR2 Adc_raw_func&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S48:                                                        '2900mV&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S44 : ADR2 Null&lt;br /&gt;
                   ADR2 Adc_batt_func&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S50:&lt;br /&gt;
        ADR2 S40 : ADR2 Null : ADR2 Null : ADR2 S51&lt;br /&gt;
        ADR2 Null&lt;br /&gt;
        Data &amp;quot;5 Options&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S51:&lt;br /&gt;
           ADR2 Null : ADR2 S52 : ADR2 S50 : ADR2 S56&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
S52:&lt;br /&gt;
           ADR2 S51 : ADR2 S53 : ADR2 S50 : ADR2 S57&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S53:&lt;br /&gt;
           ADR2 S52 : ADR2 S54 : ADR2 S50 : ADR2 S83&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S54:&lt;br /&gt;
           ADR2 S53 : ADR2 S55 : ADR2 S50 : ADR2 S58&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S55:&lt;br /&gt;
           ADR2 S54 : ADR2 Null : ADR2 S50 : ADR2 S59&lt;br /&gt;
           ADR2 Null&lt;br /&gt;
           Data &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
S56:                                                        '0...15&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S51 : ADR2 Null&lt;br /&gt;
                   ADR2 Setcontrast&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S57:&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S52 : ADR2 S87&lt;br /&gt;
                   ADR2 Null&lt;br /&gt;
                   Data &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
&lt;br /&gt;
S58:                                                        'OFF/5..90min&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S54 : ADR2 Null&lt;br /&gt;
                   ADR2 Autopower&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
S59:                                                        'ON/OFF&lt;br /&gt;
                   ADR2 Null : ADR2 Null : ADR2 S55 : ADR2 Null&lt;br /&gt;
                   ADR2 Keyclick_set&lt;br /&gt;
                   Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenüs zu State 25 bis 28&lt;br /&gt;
S65:                                                        'sub for &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
                          ADR2 S67 : ADR2 Null : ADR2 S23 : ADR2 Null&lt;br /&gt;
                          ADR2 Setclock&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S66:                                                        'sub for &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 S68 : ADR2 S24 : ADR2 Null&lt;br /&gt;
                          ADR2 Setdate&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S67:                                                        'sub for &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 S65 : ADR2 S23 : ADR2 Null&lt;br /&gt;
                          ADR2 Setclockformat&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S68:                                                        'sub for  &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
                          ADR2 S66 : ADR2 Null : ADR2 S24 : ADR2 Null&lt;br /&gt;
                          ADR2 Setdateformat&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenüs zu State 36 und 38&lt;br /&gt;
S76:                                                        'sub for  &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S36 : ADR2 Null&lt;br /&gt;
                          ADR2 Datalogger_erase&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S78:                                                        'sub for  &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S38 : ADR2 Null&lt;br /&gt;
                          ADR2 Datalogger_rs232&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
'Untermenü zu State 53 und 57&lt;br /&gt;
S83:                                                        'sub for  &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S53 : ADR2 Null&lt;br /&gt;
                          ADR2 Power_off_func&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
S87:                                                        'sub for &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
                          ADR2 Null : ADR2 Null : ADR2 S57 : ADR2 Null&lt;br /&gt;
                          ADR2 Bootfunc&lt;br /&gt;
                          Data &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
    State_renew = 0&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = Loadlabel(s32)                                    'St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
  Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = Loadlabel(s34)                                   'St_datalogger_rs232 as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
   Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = Loadlabel(s10)                                   'St_avrbf as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
   Gosub Change_state&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = Loadlabel(s53)                                    'Return  State is St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]  mit Hinweisen zu einer [[Sourcevergleich#Bascom (State Machine)|State Machine in BASCOM]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
* AVR Butterfly Application code port to avr-gcc [[http://www.siwawi.arubi.uni-kl.de/avr_projects/#bf_app]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13735</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13735"/>
				<updated>2008-07-25T13:07:08Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly [[http://www.mikrocontroller.net/articles/AVR_Butterfly]] (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesamten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
Als Beispiel wird hier die um die Umsetzung '''des Menüs aus dem AVR Butterfly Evaluation Kit in Bascom''' gezeigt:&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 1: unübersichtlich und wenig Code ==&lt;br /&gt;
Der Code ist die 1:1 Umsetzung des C-Codes aus dem AVR Butterfly Evaluation Kit.&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
* BASCOM 1.11.8.7 [Probleme mir der LCD Darstellung]&lt;br /&gt;
* BASCOM 1.11.8.1 [OK]&lt;br /&gt;
&lt;br /&gt;
Die Codelänge des Samples beträgt ca. 2300Byte. Der größte Teil geht für die LCD-Anzeige, Anzeigetexte etc. drauf. Der State Machine selber ist sehr schlank, da nur Pointer (Zeiger) gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 2: übersichtlich und viel Code ==&lt;br /&gt;
Die Variante 1 ist leider unübersichtlich, da 3 Tabellen getrennt geführt werden (die State Machine, die Menütexte und die zugehörigen Unterprogrammeinsprünge).&lt;br /&gt;
Nachfolgend eine Variante, in der nur eine Tabelle (Select Case State...) geführt wird. In jedem Statement Blocks steht ein Menütext (falls erforderlich), der Pointer für den Unterprogrammeinsprung (falls erforderlich) und ein Datensatz für die Verzweigung der State Machine (zwingend erforderlich).&lt;br /&gt;
&lt;br /&gt;
Den Pointer auf die Datensätze (DATA ...) werden ASM dynamisch generiert, um das unnötige setzen von Labels zu vermeiden. (Fehlerquelle). Mit dem Befehl ICALL werden die zugehörigen Unterprogramme über die Labeladressen angesprungen.&lt;br /&gt;
Die Datensätze (DATA..) können nach Bedarf aufgebohrt werden, indem für die Unterprogramme Zusatzinformationen abgelegt werden (Wertebereich, Incr/Decr. etc.). Hier muss beachtet werden, dass Bascom das Z-Register bei Stringoperationen etc. überschreibt, d.h. die zuzätzlichen Datensätze sollten am Anfang der Unterprogramme ausgelesen werden, solange das Z-Register noch steht.&lt;br /&gt;
 &lt;br /&gt;
Die Codelänge des Samples beträgt jetzt immerhin 3300Byte anstatt der 2300Byte von Variante 1. Dafür ist das Menü deutlich einfacher zu editieren.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
Die Bedienung erfolgt wie bei Variante 1.&lt;br /&gt;
&lt;br /&gt;
Da jetzt zur Vereinfachung nur noch Nummern für die States geführt werden, empfiehlt es sich ein Plan (siehe Grafik oben) anzulegen und jeden Kasten mit einer Nummer zu versehen. Die Zuordnung der Status-Nummern entsprechend der Tasten-Verzweigung (UP/DOWN etc.) ist dann schnell abgelesen.&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.9.1&lt;br /&gt;
'  Codelänge 3288 Byte&lt;br /&gt;
&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in dem blauen &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code:&lt;br /&gt;
&lt;br /&gt;
Dim Keycode_string As String * 4&lt;br /&gt;
'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
Keycode_string = &amp;quot;{056}{050}{052}{054}&amp;quot;&lt;br /&gt;
Dim Keycode(4) As Byte At Keycode_string Overlay&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcd = 16 * 1&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
Dim Gosub_adr As Word&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State_renew = 1&lt;br /&gt;
State = 10                                                  'Startbildschirm&lt;br /&gt;
Key = Key_null&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden und ggf. State wechseln&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Gosub State_detect                                    'hier nur Tastendruck auswerten&lt;br /&gt;
      For I = 1 To 4&lt;br /&gt;
        'Read J&lt;br /&gt;
        LPM R24,Z+&lt;br /&gt;
        sts {J},R24&lt;br /&gt;
        If Key = Keycode(i) Then&lt;br /&gt;
           If J &amp;gt; 0 Then&lt;br /&gt;
              State_renew = 1&lt;br /&gt;
              Key = Key_null                                'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
              State = J&lt;br /&gt;
           End If&lt;br /&gt;
        End If&lt;br /&gt;
      Next I&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Unterprogramm des aktuellen State auslesen und ggf. anspringen&lt;br /&gt;
   Gosub State_detect&lt;br /&gt;
   If Gosub_adr &amp;gt; 0 Then&lt;br /&gt;
      Gosub_adr = Gosub_adr / 2                             'see Bascom-Doc Mixing ASM and BASIC&lt;br /&gt;
      LDS R31, {Gosub_Adr+1}                                'High&lt;br /&gt;
      LDS R30, {Gosub_Adr}                                  'Low&lt;br /&gt;
      'Call zum Sub&lt;br /&gt;
      ICALL&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Tastaturabfrage mit Halt nur für Simulator, ansonsten Sleep+Timer_ISR verwenden!!&lt;br /&gt;
    Key = Waitkey()&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* State routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: State_detect&lt;br /&gt;
'Call from:  main loop&lt;br /&gt;
'Purpose:    Status der State Machine feststellen&lt;br /&gt;
'Result:     Pointer auf &amp;quot;DATA KeyCodes&amp;quot; und zugehörige Gosub&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
State_detect:&lt;br /&gt;
   Gosub_adr = 0&lt;br /&gt;
   Select Case State&lt;br /&gt;
     Case 10:&lt;br /&gt;
         Lcd_textbuffer = &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
         !Call Restore_label&lt;br /&gt;
         Data 0 , 20 , 0 , 11                               'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
     Case 11:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;11 Rev 2&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 0 , 0 , 10 , 0                            'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
     Case 20:&lt;br /&gt;
         Lcd_textbuffer = &amp;quot;2 Time&amp;quot;&lt;br /&gt;
         !Call Restore_label&lt;br /&gt;
         Data 10 , 30 , 0 , 21&lt;br /&gt;
     Case 21:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 0 , 22 , 20 , 23&lt;br /&gt;
     Case 22:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;22 Date&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 21 , 0 , 20 , 24&lt;br /&gt;
     Case 23:                                               'Show HH:MM:SS&lt;br /&gt;
                   Gosub_adr = Loadlabel(showclock)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 24 , 21 , 25&lt;br /&gt;
     Case 24:                                               'Show DD:MM:YY&lt;br /&gt;
                   Gosub_adr = Loadlabel(showdate)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 23 , 0 , 22 , 26&lt;br /&gt;
     Case 25:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 27 , 0 , 23 , 65&lt;br /&gt;
     Case 26:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 28 , 24 , 66&lt;br /&gt;
     Case 27:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 25 , 23 , 67&lt;br /&gt;
     Case 28:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 26 , 0 , 23 , 68&lt;br /&gt;
     Case 30:&lt;br /&gt;
         Lcd_textbuffer = &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
         !Call Restore_label&lt;br /&gt;
         Data 20 , 40 , 0 , 31&lt;br /&gt;
     Case 31:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 0 , 32 , 30 , 35&lt;br /&gt;
     Case 32:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 31 , 33 , 30 , 36&lt;br /&gt;
     Case 33:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 32 , 34 , 30 , 37&lt;br /&gt;
     Case 34:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 33 , 0 , 30 , 38&lt;br /&gt;
     Case 35:                                               'HH:MM&lt;br /&gt;
                   Gosub_adr = Loadlabel(datalogger_setloginterval)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 31 , 0&lt;br /&gt;
     Case 36:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 32 , 76&lt;br /&gt;
     Case 37:                                               '1234&lt;br /&gt;
                   Gosub_adr = Loadlabel(datalogger_logcount)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 33 , 0&lt;br /&gt;
     Case 38:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 34 , 78&lt;br /&gt;
     Case 40:&lt;br /&gt;
        Lcd_textbuffer = &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
        !Call Restore_label&lt;br /&gt;
        Data 30 , 50 , 0 , 41&lt;br /&gt;
     Case 41:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
            !Call Restore_label&lt;br /&gt;
            Data 0 , 42 , 40 , 45&lt;br /&gt;
     Case 42:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 41 , 43 , 40 , 46&lt;br /&gt;
     Case 43:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
            !Call Restore_label&lt;br /&gt;
            Data 42 , 44 , 40 , 47&lt;br /&gt;
     Case 44:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 43 , 0 , 40 , 48&lt;br /&gt;
     Case 45:                                               '+24°C&lt;br /&gt;
                   Gosub_adr = Loadlabel(temperaturefunc)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 41 , 0&lt;br /&gt;
     Case 46:                                               '0 mV&lt;br /&gt;
                   Gosub_adr = Loadlabel(voltagefunc)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 42 , 0&lt;br /&gt;
     Case 47:                                               'CH:RAW&lt;br /&gt;
                   Gosub_adr = Loadlabel(adc_raw_func)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 43 , 0&lt;br /&gt;
     Case 48:                                               '2900mV&lt;br /&gt;
                   Gosub_adr = Loadlabel(adc_batt_func)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 44 , 0&lt;br /&gt;
     Case 50:&lt;br /&gt;
        Lcd_textbuffer = &amp;quot;5 Options&amp;quot;&lt;br /&gt;
        !Call Restore_label&lt;br /&gt;
        Data 40 , 0 , 0 , 51&lt;br /&gt;
     Case 51:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 0 , 52 , 50 , 56&lt;br /&gt;
     Case 52:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 51 , 53 , 50 , 57&lt;br /&gt;
     Case 53:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 52 , 54 , 50 , 83&lt;br /&gt;
     Case 54:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 53 , 55 , 50 , 58&lt;br /&gt;
     Case 55:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 54 , 0 , 50 , 59&lt;br /&gt;
     Case 56:                                               '0...15&lt;br /&gt;
                   Gosub_adr = Loadlabel(setcontrast)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 51 , 0&lt;br /&gt;
     Case 57:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 52 , 87&lt;br /&gt;
     Case 58:                                               'OFF/5..90min&lt;br /&gt;
                   Gosub_adr = Loadlabel(autopower)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 54 , 0&lt;br /&gt;
     Case 59:                                               'ON/OFF&lt;br /&gt;
                   Gosub_adr = Loadlabel(keyclick_set)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 55 , 0&lt;br /&gt;
'Untermenüs zu State 25 bis 28&lt;br /&gt;
     Case 65:                                               'sub for &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(setclock)&lt;br /&gt;
                         !Call Restore_label&lt;br /&gt;
                          Data 67 , 0 , 23 , 0&lt;br /&gt;
     Case 66:                                               'sub for &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(setdate)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 68 , 24 , 0&lt;br /&gt;
     Case 67:                                               'sub for &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(setclockformat)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 65 , 23 , 0&lt;br /&gt;
     Case 68:                                               'sub for  &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(setdateformat)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 66 , 0 , 24 , 0&lt;br /&gt;
'Untermenüs zu State 36 und 38&lt;br /&gt;
     Case 76:                                               'sub for  &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(datalogger_erase)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 0 , 36 , 0&lt;br /&gt;
     Case 78:                                               'sub for  &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(datalogger_rs232)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 0 , 38 , 0&lt;br /&gt;
'Untermenü zu State 53 und 57&lt;br /&gt;
     Case 83:                                               'sub for  &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(power_off_func)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 0 , 53 , 0&lt;br /&gt;
     Case 87:                                               'sub for &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(bootfunc)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 0 , 57 , 0&lt;br /&gt;
'Error Handling&lt;br /&gt;
     Case Else&lt;br /&gt;
        State = 10&lt;br /&gt;
        !Call Restore_label&lt;br /&gt;
        Data 0 , 0 , 0 , 0&lt;br /&gt;
&lt;br /&gt;
 End Select&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Restore_label&lt;br /&gt;
'Call from:  State_detect&lt;br /&gt;
'Purpose:    Ersatz für RESTORE Label, erspart das Setzen der Labels für DATA&lt;br /&gt;
'Result:     Pointer ZH:ZL steht auf der Rücksprungadresse =&amp;gt; DATA aa,bb,cc,dd&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Restore_label:&lt;br /&gt;
   Pop ZH                                                   'R31&lt;br /&gt;
   POP ZL                                                   'R30&lt;br /&gt;
   LSL ZL                                                   'Multipliziere Adr mit 2&lt;br /&gt;
   ROL ZH&lt;br /&gt;
   'this is a return from Sub State_detect to main loop&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
    State_renew = 0&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = 32                                                'St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = 34                                               'St_datalogger_rs232 as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = 10                                               'St_avrbf as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = 53                                                'Return  State is St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]  mit Hinweisen zu einer [[Sourcevergleich#Bascom (State Machine)|State Machine in BASCOM]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
* AVR Butterfly Application code port to avr-gcc [[http://www.siwawi.arubi.uni-kl.de/avr_projects/#bf_app]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13734</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13734"/>
				<updated>2008-07-25T12:51:52Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly [[http://www.mikrocontroller.net/articles/AVR_Butterfly]] (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesamten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
Als Beispiel wird hier die um die Umsetzung des Menüs aus dem AVR Butterfly Evaluation Kit in Bascom gezeigt:&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 1: unübersichtlich und wenig Code ==&lt;br /&gt;
Der Code ist die 1:1 Umsetzung des C-Codes aus dem AVR Butterfly Evaluation Kit.&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
* BASCOM 1.11.8.7 [Probleme mir der LCD Darstellung]&lt;br /&gt;
* BASCOM 1.11.8.1 [OK]&lt;br /&gt;
&lt;br /&gt;
Die Codelänge des Samples beträgt ca. 2300Byte. Der größte Teil geht für die LCD-Anzeige, Anzeigetexte etc. drauf. Der State Machine selber ist sehr schlank, da nur Pointer (Zeiger) gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 2: übersichtlich und viel Code ==&lt;br /&gt;
Die Variante 1 ist leider unübersichtlich, da 3 Tabellen getrennt geführt werden (die State Machine, die Menütexte und die zugehörigen Unterprogrammeinsprünge).&lt;br /&gt;
Nachfolgend eine Variante, in der nur eine Tabelle (Select Case State...) geführt wird. In jedem Statement Blocks steht ein Menütext (falls erforderlich), der Pointer für den Unterprogrammeinsprung (falls erforderlich) und ein Datensatz für die Verzweigung der State Machine (zwingend erforderlich).&lt;br /&gt;
 &lt;br /&gt;
Die Codelänge des Samples beträgt jetzt immerhin 3300Byte anstatt der 2300Byte von Variante 1. Dafür ist das Menü deutlich einfacher zu editieren.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
Die Bedienung erfolgt wie bei Variante 1.&lt;br /&gt;
&lt;br /&gt;
Da jetzt zur Vereinfachung nur noch Nummern für die States geführt werden, empfiehlt es sich ein Plan (siehe Grafik oben) anzulegen und jeden Kasten mit einer Nummer zu versehen. Die Zuordnung der Status-Nummern entsprechend der Tasten-Verzweigung (UP/DOWN etc.) ist dann schnell abgelesen.&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.9.1&lt;br /&gt;
'  Codelänge 3288 Byte&lt;br /&gt;
&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in dem blauen &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code:&lt;br /&gt;
&lt;br /&gt;
Dim Keycode_string As String * 4&lt;br /&gt;
'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
Keycode_string = &amp;quot;{056}{050}{052}{054}&amp;quot;&lt;br /&gt;
Dim Keycode(4) As Byte At Keycode_string Overlay&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcd = 16 * 1&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
Dim Gosub_adr As Word&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State_renew = 1&lt;br /&gt;
State = 10                                                  'Startbildschirm&lt;br /&gt;
Key = Key_null&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden und ggf. State wechseln&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Gosub State_detect                                    'hier nur Tastendruck auswerten&lt;br /&gt;
      For I = 1 To 4&lt;br /&gt;
        'Read J&lt;br /&gt;
        LPM R24,Z+&lt;br /&gt;
        sts {J},R24&lt;br /&gt;
        If Key = Keycode(i) Then&lt;br /&gt;
           If J &amp;gt; 0 Then&lt;br /&gt;
              State_renew = 1&lt;br /&gt;
              Key = Key_null                                'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
              State = J&lt;br /&gt;
           End If&lt;br /&gt;
        End If&lt;br /&gt;
      Next I&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Unterprogramm des aktuellen State auslesen und ggf. anspringen&lt;br /&gt;
   Gosub State_detect&lt;br /&gt;
   If Gosub_adr &amp;gt; 0 Then&lt;br /&gt;
      Gosub_adr = Gosub_adr / 2                             'see Bascom-Doc Mixing ASM and BASIC&lt;br /&gt;
      LDS R31, {Gosub_Adr+1}                                'High&lt;br /&gt;
      LDS R30, {Gosub_Adr}                                  'Low&lt;br /&gt;
      'Call zum Sub&lt;br /&gt;
      ICALL&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Tastaturabfrage mit Halt nur für Simulator, ansonsten Sleep+Timer_ISR verwenden!!&lt;br /&gt;
    Key = Waitkey()&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* State routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: State_detect&lt;br /&gt;
'Call from:  main loop&lt;br /&gt;
'Purpose:    Status der State Machine feststellen&lt;br /&gt;
'Result:     Pointer auf &amp;quot;DATA KeyCodes&amp;quot; und zugehörige Gosub&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
State_detect:&lt;br /&gt;
   Gosub_adr = 0&lt;br /&gt;
   Select Case State&lt;br /&gt;
     Case 10:&lt;br /&gt;
         Lcd_textbuffer = &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
         !Call Restore_label&lt;br /&gt;
         Data 0 , 20 , 0 , 11                               'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
     Case 11:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;11 Rev 2&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 0 , 0 , 10 , 0                            'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
     Case 20:&lt;br /&gt;
         Lcd_textbuffer = &amp;quot;2 Time&amp;quot;&lt;br /&gt;
         !Call Restore_label&lt;br /&gt;
         Data 10 , 30 , 0 , 21&lt;br /&gt;
     Case 21:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 0 , 22 , 20 , 23&lt;br /&gt;
     Case 22:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;22 Date&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 21 , 0 , 20 , 24&lt;br /&gt;
     Case 23:                                               'Show HH:MM:SS&lt;br /&gt;
                   Gosub_adr = Loadlabel(showclock)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 24 , 21 , 25&lt;br /&gt;
     Case 24:                                               'Show DD:MM:YY&lt;br /&gt;
                   Gosub_adr = Loadlabel(showdate)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 23 , 0 , 22 , 26&lt;br /&gt;
     Case 25:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 27 , 0 , 23 , 65&lt;br /&gt;
     Case 26:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 28 , 24 , 66&lt;br /&gt;
     Case 27:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 25 , 23 , 67&lt;br /&gt;
     Case 28:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 26 , 0 , 23 , 68&lt;br /&gt;
     Case 30:&lt;br /&gt;
         Lcd_textbuffer = &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
         !Call Restore_label&lt;br /&gt;
         Data 20 , 40 , 0 , 31&lt;br /&gt;
     Case 31:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 0 , 32 , 30 , 35&lt;br /&gt;
     Case 32:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 31 , 33 , 30 , 36&lt;br /&gt;
     Case 33:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 32 , 34 , 30 , 37&lt;br /&gt;
     Case 34:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 33 , 0 , 30 , 38&lt;br /&gt;
     Case 35:                                               'HH:MM&lt;br /&gt;
                   Gosub_adr = Loadlabel(datalogger_setloginterval)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 31 , 0&lt;br /&gt;
     Case 36:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 32 , 76&lt;br /&gt;
     Case 37:                                               '1234&lt;br /&gt;
                   Gosub_adr = Loadlabel(datalogger_logcount)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 33 , 0&lt;br /&gt;
     Case 38:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 34 , 78&lt;br /&gt;
     Case 40:&lt;br /&gt;
        Lcd_textbuffer = &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
        !Call Restore_label&lt;br /&gt;
        Data 30 , 50 , 0 , 41&lt;br /&gt;
     Case 41:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
            !Call Restore_label&lt;br /&gt;
            Data 0 , 42 , 40 , 45&lt;br /&gt;
     Case 42:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 41 , 43 , 40 , 46&lt;br /&gt;
     Case 43:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
            !Call Restore_label&lt;br /&gt;
            Data 42 , 44 , 40 , 47&lt;br /&gt;
     Case 44:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 43 , 0 , 40 , 48&lt;br /&gt;
     Case 45:                                               '+24°C&lt;br /&gt;
                   Gosub_adr = Loadlabel(temperaturefunc)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 41 , 0&lt;br /&gt;
     Case 46:                                               '0 mV&lt;br /&gt;
                   Gosub_adr = Loadlabel(voltagefunc)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 42 , 0&lt;br /&gt;
     Case 47:                                               'CH:RAW&lt;br /&gt;
                   Gosub_adr = Loadlabel(adc_raw_func)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 43 , 0&lt;br /&gt;
     Case 48:                                               '2900mV&lt;br /&gt;
                   Gosub_adr = Loadlabel(adc_batt_func)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 44 , 0&lt;br /&gt;
     Case 50:&lt;br /&gt;
        Lcd_textbuffer = &amp;quot;5 Options&amp;quot;&lt;br /&gt;
        !Call Restore_label&lt;br /&gt;
        Data 40 , 0 , 0 , 51&lt;br /&gt;
     Case 51:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 0 , 52 , 50 , 56&lt;br /&gt;
     Case 52:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 51 , 53 , 50 , 57&lt;br /&gt;
     Case 53:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 52 , 54 , 50 , 83&lt;br /&gt;
     Case 54:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 53 , 55 , 50 , 58&lt;br /&gt;
     Case 55:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 54 , 0 , 50 , 59&lt;br /&gt;
     Case 56:                                               '0...15&lt;br /&gt;
                   Gosub_adr = Loadlabel(setcontrast)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 51 , 0&lt;br /&gt;
     Case 57:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 52 , 87&lt;br /&gt;
     Case 58:                                               'OFF/5..90min&lt;br /&gt;
                   Gosub_adr = Loadlabel(autopower)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 54 , 0&lt;br /&gt;
     Case 59:                                               'ON/OFF&lt;br /&gt;
                   Gosub_adr = Loadlabel(keyclick_set)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 55 , 0&lt;br /&gt;
'Untermenüs zu State 25 bis 28&lt;br /&gt;
     Case 65:                                               'sub for &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(setclock)&lt;br /&gt;
                         !Call Restore_label&lt;br /&gt;
                          Data 67 , 0 , 23 , 0&lt;br /&gt;
     Case 66:                                               'sub for &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(setdate)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 68 , 24 , 0&lt;br /&gt;
     Case 67:                                               'sub for &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(setclockformat)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 65 , 23 , 0&lt;br /&gt;
     Case 68:                                               'sub for  &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(setdateformat)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 66 , 0 , 24 , 0&lt;br /&gt;
'Untermenüs zu State 36 und 38&lt;br /&gt;
     Case 76:                                               'sub for  &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(datalogger_erase)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 0 , 36 , 0&lt;br /&gt;
     Case 78:                                               'sub for  &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(datalogger_rs232)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 0 , 38 , 0&lt;br /&gt;
'Untermenü zu State 53 und 57&lt;br /&gt;
     Case 83:                                               'sub for  &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(power_off_func)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 0 , 53 , 0&lt;br /&gt;
     Case 87:                                               'sub for &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(bootfunc)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 0 , 57 , 0&lt;br /&gt;
'Error Handling&lt;br /&gt;
     Case Else&lt;br /&gt;
        State = 10&lt;br /&gt;
        !Call Restore_label&lt;br /&gt;
        Data 0 , 0 , 0 , 0&lt;br /&gt;
&lt;br /&gt;
 End Select&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Restore_label&lt;br /&gt;
'Call from:  State_detect&lt;br /&gt;
'Purpose:    Ersatz für RESTORE Label, erspart das Setzen der Labels für DATA&lt;br /&gt;
'Result:     Pointer ZH:ZL steht auf der Rücksprungadresse =&amp;gt; DATA aa,bb,cc,dd&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Restore_label:&lt;br /&gt;
   Pop ZH                                                   'R31&lt;br /&gt;
   POP ZL                                                   'R30&lt;br /&gt;
   LSL ZL                                                   'Multipliziere Adr mit 2&lt;br /&gt;
   ROL ZH&lt;br /&gt;
   'this is a return from Sub State_detect to main loop&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
    State_renew = 0&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = 32                                                'St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = 34                                               'St_datalogger_rs232 as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = 10                                               'St_avrbf as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = 53                                                'Return  State is St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]  mit Hinweisen zu einer [[Sourcevergleich#Bascom (State Machine)|State Machine in BASCOM]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
* AVR Butterfly Application code port to avr-gcc [[http://www.siwawi.arubi.uni-kl.de/avr_projects/#bf_app]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13731</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13731"/>
				<updated>2008-07-25T12:02:11Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: Variante 2 ergänzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly [[http://www.mikrocontroller.net/articles/AVR_Butterfly]] (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesamten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
Als Beispiel wird hier die um die Umsetzung des Menüs aus dem AVR Butterfly Evaluation Kit in Bascom gezeigt:&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 1: unübersichtlich und wenig Code ==&lt;br /&gt;
Der Code ist die 1:1 Umsetzung des C-Codes aus dem AVR Butterfly Evaluation Kit.&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
* BASCOM 1.11.8.7 [Probleme mir der LCD Darstellung]&lt;br /&gt;
* BASCOM 1.11.8.1 [OK]&lt;br /&gt;
&lt;br /&gt;
Die Codelänge des Samples beträgt ca. 2300Byte. Der größte Teil geht für die LCD-Anzeige, Anzeigetexte etc. drauf. Der State Machine selber ist sehr schlank, da nur Pointer (Zeiger) gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sample Code Variante 2: übersichtlich und viel Code ==&lt;br /&gt;
Die Variante 1 ist leider unübersichtlich, da 3 Tabellen getrennt geführt werden (die State Machine, die Menütexte und die zugehörigen Unterprogrammeinsprünge).&lt;br /&gt;
Nachfolgend eine Variante, in der nur eine Tabelle (Select Case State...) geführt wird. In jedem Statement Blocks steht ein Menütext (falls erforderlich), der Pointer für den Unterprogrammeinsprung (falls erforderlich) und ein Datensatz für die Verzweigung der State Machine (zwingend erforderlich).&lt;br /&gt;
 &lt;br /&gt;
Die Codelänge des Samples beträgt jetzt immerhin 3300Byte anstatt der 2300Byte von Variante 1. Dafür ist das Menü deutlich einfacher zu editieren.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
Die Bedienung erfolgt wie bei Variante 1.&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.9.1&lt;br /&gt;
'  Codelänge 3288 Byte&lt;br /&gt;
&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in dem blauen &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code:&lt;br /&gt;
&lt;br /&gt;
Dim Keycode_string As String * 4&lt;br /&gt;
'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
Keycode_string = &amp;quot;{056}{050}{052}{054}&amp;quot;&lt;br /&gt;
Dim Keycode(4) As Byte At Keycode_string Overlay&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcd = 16 * 1&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
Dim Gosub_adr As Word&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State_renew = 1&lt;br /&gt;
State = 10                                                  'Startbildschirm&lt;br /&gt;
Key = Key_null&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden und ggf. State wechseln&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Gosub State_detect                                    'hier nur Tastendruck auswerten&lt;br /&gt;
      For I = 1 To 4&lt;br /&gt;
        'Read J&lt;br /&gt;
        LPM R24,Z+&lt;br /&gt;
        sts {J},R24&lt;br /&gt;
        If Key = Keycode(i) Then&lt;br /&gt;
           If J &amp;gt; 0 Then&lt;br /&gt;
              State_renew = 1&lt;br /&gt;
              Key = Key_null                                'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
              State = J&lt;br /&gt;
           End If&lt;br /&gt;
        End If&lt;br /&gt;
      Next I&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Unterprogramm des aktuellen State auslesen und ggf. anspringen&lt;br /&gt;
   Gosub State_detect&lt;br /&gt;
   If Gosub_adr &amp;gt; 0 Then&lt;br /&gt;
      Gosub_adr = Gosub_adr / 2                             'see Bascom-Doc Mixing ASM and BASIC&lt;br /&gt;
      LDS R31, {Gosub_Adr+1}                                'High&lt;br /&gt;
      LDS R30, {Gosub_Adr}                                  'Low&lt;br /&gt;
      'Call zum Sub&lt;br /&gt;
      ICALL&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Tastaturabfrage mit Halt nur für Simulator, ansonsten Sleep+Timer_ISR verwenden!!&lt;br /&gt;
    Key = Waitkey()&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* State routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: State_detect&lt;br /&gt;
'Call from:  main loop&lt;br /&gt;
'Purpose:    Status der State Machine feststellen&lt;br /&gt;
'Result:     Pointer auf &amp;quot;DATA KeyCodes&amp;quot; und zugehörige Gosub&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
State_detect:&lt;br /&gt;
   Gosub_adr = 0&lt;br /&gt;
   Select Case State&lt;br /&gt;
     Case 10:&lt;br /&gt;
         Lcd_textbuffer = &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
         !Call Restore_label&lt;br /&gt;
         Data 0 , 20 , 0 , 11                               'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
     Case 11:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;11 Rev 2&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 0 , 0 , 10 , 0                            'Key_plus|Key_minus|Key_prev|Key_next&lt;br /&gt;
     Case 20:&lt;br /&gt;
         Lcd_textbuffer = &amp;quot;2 Time&amp;quot;&lt;br /&gt;
         !Call Restore_label&lt;br /&gt;
         Data 10 , 30 , 0 , 21&lt;br /&gt;
     Case 21:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 0 , 22 , 20 , 23&lt;br /&gt;
     Case 22:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;22 Date&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 21 , 0 , 20 , 24&lt;br /&gt;
     Case 23:                                               'Show HH:MM:SS&lt;br /&gt;
                   Gosub_adr = Loadlabel(showclock)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 24 , 21 , 25&lt;br /&gt;
     Case 24:                                               'Show DD:MM:YY&lt;br /&gt;
                   Gosub_adr = Loadlabel(showdate)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 23 , 0 , 22 , 26&lt;br /&gt;
     Case 25:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 27 , 0 , 23 , 65&lt;br /&gt;
     Case 26:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 28 , 24 , 66&lt;br /&gt;
     Case 27:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 25 , 23 , 67&lt;br /&gt;
     Case 28:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 26 , 0 , 23 , 68&lt;br /&gt;
     Case 30:&lt;br /&gt;
         Lcd_textbuffer = &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
         !Call Restore_label&lt;br /&gt;
         Data 20 , 40 , 0 , 31&lt;br /&gt;
     Case 31:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 0 , 32 , 30 , 35&lt;br /&gt;
     Case 32:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 31 , 33 , 30 , 36&lt;br /&gt;
     Case 33:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 32 , 34 , 30 , 37&lt;br /&gt;
     Case 34:&lt;br /&gt;
             Lcd_textbuffer = &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
             !Call Restore_label&lt;br /&gt;
             Data 33 , 0 , 30 , 38&lt;br /&gt;
     Case 35:                                               'HH:MM&lt;br /&gt;
                   Gosub_adr = Loadlabel(datalogger_setloginterval)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 31 , 0&lt;br /&gt;
     Case 36:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 32 , 76&lt;br /&gt;
     Case 37:                                               '1234&lt;br /&gt;
                   Gosub_adr = Loadlabel(datalogger_logcount)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 33 , 0&lt;br /&gt;
     Case 38:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 34 , 78&lt;br /&gt;
     Case 40:&lt;br /&gt;
        Lcd_textbuffer = &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
        !Call Restore_label&lt;br /&gt;
        Data 30 , 50 , 0 , 41&lt;br /&gt;
     Case 41:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
            !Call Restore_label&lt;br /&gt;
            Data 0 , 42 , 40 , 45&lt;br /&gt;
     Case 42:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 41 , 43 , 40 , 46&lt;br /&gt;
     Case 43:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
            !Call Restore_label&lt;br /&gt;
            Data 42 , 44 , 40 , 47&lt;br /&gt;
     Case 44:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 43 , 0 , 40 , 48&lt;br /&gt;
     Case 45:                                               '+24°C&lt;br /&gt;
                   Gosub_adr = Loadlabel(temperaturefunc)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 41 , 0&lt;br /&gt;
     Case 46:                                               '0 mV&lt;br /&gt;
                   Gosub_adr = Loadlabel(voltagefunc)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 42 , 0&lt;br /&gt;
     Case 47:                                               'CH:RAW&lt;br /&gt;
                   Gosub_adr = Loadlabel(adc_raw_func)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 43 , 0&lt;br /&gt;
     Case 48:                                               '2900mV&lt;br /&gt;
                   Gosub_adr = Loadlabel(adc_batt_func)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 44 , 0&lt;br /&gt;
     Case 50:&lt;br /&gt;
        Lcd_textbuffer = &amp;quot;5 Options&amp;quot;&lt;br /&gt;
        !Call Restore_label&lt;br /&gt;
        Data 40 , 0 , 0 , 51&lt;br /&gt;
     Case 51:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 0 , 52 , 50 , 56&lt;br /&gt;
     Case 52:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 51 , 53 , 50 , 57&lt;br /&gt;
     Case 53:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 52 , 54 , 50 , 83&lt;br /&gt;
     Case 54:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 53 , 55 , 50 , 58&lt;br /&gt;
     Case 55:&lt;br /&gt;
            Lcd_textbuffer = &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
           !Call Restore_label&lt;br /&gt;
           Data 54 , 0 , 50 , 59&lt;br /&gt;
     Case 56:                                               '0...15&lt;br /&gt;
                   Gosub_adr = Loadlabel(setcontrast)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 51 , 0&lt;br /&gt;
     Case 57:&lt;br /&gt;
                   Lcd_textbuffer = &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 52 , 87&lt;br /&gt;
     Case 58:                                               'OFF/5..90min&lt;br /&gt;
                   Gosub_adr = Loadlabel(autopower)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 54 , 0&lt;br /&gt;
     Case 59:                                               'ON/OFF&lt;br /&gt;
                   Gosub_adr = Loadlabel(keyclick_set)&lt;br /&gt;
                   !Call Restore_label&lt;br /&gt;
                   Data 0 , 0 , 55 , 0&lt;br /&gt;
'Untermenüs zu State 25 bis 28&lt;br /&gt;
     Case 65:                                               'sub for &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(setclock)&lt;br /&gt;
                         !Call Restore_label&lt;br /&gt;
                          Data 67 , 0 , 23 , 0&lt;br /&gt;
     Case 66:                                               'sub for &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(setdate)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 68 , 24 , 0&lt;br /&gt;
     Case 67:                                               'sub for &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(setclockformat)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 65 , 23 , 0&lt;br /&gt;
     Case 68:                                               'sub for  &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(setdateformat)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 66 , 0 , 24 , 0&lt;br /&gt;
'Untermenüs zu State 36 und 38&lt;br /&gt;
     Case 76:                                               'sub for  &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(datalogger_erase)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 0 , 36 , 0&lt;br /&gt;
     Case 78:                                               'sub for  &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(datalogger_rs232)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 0 , 38 , 0&lt;br /&gt;
'Untermenü zu State 53 und 57&lt;br /&gt;
     Case 83:                                               'sub for  &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(power_off_func)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 0 , 53 , 0&lt;br /&gt;
     Case 87:                                               'sub for &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
                          Gosub_adr = Loadlabel(bootfunc)&lt;br /&gt;
                          !Call Restore_label&lt;br /&gt;
                          Data 0 , 0 , 57 , 0&lt;br /&gt;
'Error Handling&lt;br /&gt;
     Case Else&lt;br /&gt;
        State = 10&lt;br /&gt;
        !Call Restore_label&lt;br /&gt;
        Data 0 , 0 , 0 , 0&lt;br /&gt;
&lt;br /&gt;
 End Select&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Restore_label&lt;br /&gt;
'Call from:  State_detect&lt;br /&gt;
'Purpose:    Ersatz für RESTORE Label, erspart das Setzen der Labels für DATA&lt;br /&gt;
'Result:     Pointer ZH:ZL steht auf der Rücksprungadresse =&amp;gt; DATA aa,bb,cc,dd&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Restore_label:&lt;br /&gt;
   Pop ZH                                                   'R31&lt;br /&gt;
   POP ZL                                                   'R30&lt;br /&gt;
   LSL ZL                                                   'Multipliziere Adr mit 2&lt;br /&gt;
   ROL ZH&lt;br /&gt;
   'this is a return from Sub State_detect to main loop&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
    State_renew = 0&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = 32                                                'St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = 34                                               'St_datalogger_rs232 as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = 10                                               'St_avrbf as next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = 53                                                'Return  State is St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]  mit Hinweisen zu einer [[Sourcevergleich#Bascom (State Machine)|State Machine in BASCOM]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
* AVR Butterfly Application code port to avr-gcc [[http://www.siwawi.arubi.uni-kl.de/avr_projects/#bf_app]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13613</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13613"/>
				<updated>2008-06-17T11:49:53Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: Sample Code Length eingetragen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly [[http://www.mikrocontroller.net/articles/AVR_Butterfly]] (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesamten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code ==&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
* BASCOM 1.11.8.7 [Probleme mir der LCD Darstellung]&lt;br /&gt;
* BASCOM 1.11.8.1 [OK]&lt;br /&gt;
&lt;br /&gt;
Die Codelänge des Samples beträgt ca. 2300Byte. Der größte Teil geht für die LCD-Anzeige, Anzeigetexte etc. drauf. Der State Machine selber ist sehr schlank, da nur Pointer (Zeiger) gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]  mit Hinweisen zu einer [[Sourcevergleich#Bascom (State Machine)|State Machine in BASCOM]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
* AVR Butterfly Application code port to avr-gcc [[http://www.siwawi.arubi.uni-kl.de/avr_projects/#bf_app]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13382</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=13382"/>
				<updated>2008-04-28T19:24:11Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: getestete Bascom-Versionen ergänzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly [[http://www.mikrocontroller.net/articles/AVR_Butterfly]] (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesamten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code ==&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Getestet im Bascom-Simulator der  Versionen:&lt;br /&gt;
* BASCOM 1.11.9.1 [OK]&lt;br /&gt;
* BASCOM 1.11.8.7 [Probleme mir der LCD Darstellung]&lt;br /&gt;
* BASCOM 1.11.8.1 [OK]&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]  mit Hinweisen zu einer [[Sourcevergleich#Bascom (State Machine)|State Machine in BASCOM]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
* AVR Butterfly Application code port to avr-gcc [[http://www.siwawi.arubi.uni-kl.de/avr_projects/#bf_app]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=10068</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=10068"/>
				<updated>2007-01-29T19:51:20Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly [[http://www.mikrocontroller.net/articles/AVR_Butterfly]] (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesammten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code ==&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]  mit Hinweisen zu einer [[Sourcevergleich#Bascom (State Machine)|State Machine in BASCOM]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
* AVR Butterfly Application code port to avr-gcc [[http://www.siwawi.arubi.uni-kl.de/avr_projects/#bf_app]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=8855</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=8855"/>
				<updated>2006-09-21T22:14:15Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Bascom State Machine Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des Beispielcodes von Atmel für AVR Butterfly (in IAR-C bzw. die GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde hier stark reduziert, so dass vom gesammten Programm das Menü als Rahmen mit allen Unterprogrammen (leer) übrig geblieben ist.&lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code ==&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]     mit Hinweisen zu einer State Machine in BASCOM&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=8854</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=8854"/>
				<updated>2006-09-21T22:08:49Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des AVR Butterfly IAR-C Code (bzw. der GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde stark reduziert, so dass für das gesammte Programm der Menü-Rahmen mit allen Unterprogrammen übrig geblieben ist. &lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code ==&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]     mit Hinweisen zu einer State Machine in BASCOM&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom&amp;diff=8853</id>
		<title>Bascom</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom&amp;diff=8853"/>
				<updated>2006-09-21T22:00:14Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==AVR Bascom Basic==&lt;br /&gt;
&lt;br /&gt;
Bascom ist eine komplette Basic-Entwicklungsumgebung für die verschiedensten AVR Controller.Er bietet ein ungeheuer großes Leistungsvermögen und besonders anwenderfreundliche Entwicklungsumgebung. &lt;br /&gt;
Eine kostenlose Version, die bis zu 4 KB (das ist schon einiges bei einem Controller) keinerlei Einschränkungen besitzt, findet man auf der Seite des Herstellers &lt;br /&gt;
&lt;br /&gt;
[http://www.mcselec.com/index.php?option=com_docman&amp;amp;task=cat_view&amp;amp;gid=99&amp;amp;Itemid=54 Bascom-Download]&lt;br /&gt;
&lt;br /&gt;
Nach dem Download müssen alle Dateien entpackt und das SETUP-Programm aufgerufen werden. Danach steht ein Basic-Entwicklungssystem zur Verfügung das alles beinhaltet was für die AVR-Programmierung notwendig ist. Zum Beispiel: Editor mit Befehlsvorschlag, Simulator, Terminalprogram, Avr-FuseBit Einstellung, integriertem Assembler, eingebauten Programmer zur Übertragung des Programmcode usw.&lt;br /&gt;
&lt;br /&gt;
Als erstes solltet ihr unter dem Menü Options / Compiler den Zielprozessor angeben. Fast alle gängigen AVR Controller können programmiert werden.&lt;br /&gt;
In diesem Dialog können auch noch viele weitere Einstellungen vorgenommen werden. Eigentlich ist das alles selbsterklärend. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
http://www.shop.robotikhardware.de/shop/catalog/images/artikelbilder/bascom/bascom.gif&lt;br /&gt;
&lt;br /&gt;
==Befehlsübersicht von Bascom==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Verzeigungen und Strukturbefehle===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
IF, THEN, ELSE, ELSEIF, END IF, DO, LOOP, WHILE, WEND, UNTIL,&lt;br /&gt;
EXIT DO, EXIT WHILE, FOR, NEXT, TO,  STEP, EXIT FOR,&lt;br /&gt;
ON .. GOTO/GOSUB, SELECT, CASE.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Ein- und Ausgabebefehle===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PRINT, INPUT, INKEY, PRINT, INPUTHEX, LCD, UPPERLINE, LOWERLINE,&lt;br /&gt;
DISPLAY ON/OFF, CURSOR ON/OFF/BLINK/NOBLINK, HOME, LOCATE, &lt;br /&gt;
SHIFTLCD LEFT/RIGHT, SHIFTCURSOR LEFT/RIGHT, CLS, DEFLCDCHAR, WAITKEY,&lt;br /&gt;
INPUTBIN, PRINTBIN,  OPEN, CLOSE, DEBOUNCE, SHIFTIN, SHIFTOUT,&lt;br /&gt;
GETATKBD, SPC, SERIN, SEROUT&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Mathematische Funktionen===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
AND, OR, XOR, INC, DEC, MOD, NOT, ABS, BCD, LOG, EXP, SQR, SIN,COS,&lt;br /&gt;
TAN,ATN, ATN2, ASIN, ACOS, FIX, ROUND, MOD, SGN, POWER, RAD2DEG,&lt;br /&gt;
DEG2RAD, LOG10, TANH, SINH, COSH.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===I2C-Bus===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
I2CSTART, I2CSTOP, I2CWBYTE, I2CRBYTE, I2CSEND and I2CRECEIVE.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===1WIRE Bus===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1WWRITE, 1WREAD, 1WRESET, 1WIRECOUNT, 1WSEARCHFIRST, 1WSEARCHNEXT.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===SPI-Bus===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SPIINIT, SPIIN, SPIOUT, SPIMOVE.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Datum und Zeitfunktionen===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DayOfWeek, DayOfYear, SecOfDay, SecElapsed, SysDay, SysSec, SysSecElapsed,&lt;br /&gt;
Time, Date, Time$, Date$&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Disk/Drive===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DriveReadSector, DriveWriteSector, DriveInit, DriveGetIdentity, &lt;br /&gt;
DriveReset, DriveCheck.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Dateisystem Befehle===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
InitFileSystem, DiskSize, DiskFree, Kill, Dir, Name, ChDir, MkDir, RmDir&lt;br /&gt;
FileLen, FileDateTime, FileDate, FileTime, GetAttr&lt;br /&gt;
FreeFile, Open, Close, Flush, Print, Write, Input, Line Input, Get, Put, Seek&lt;br /&gt;
EOF, LOC, LOF, FileAttr.&lt;br /&gt;
BLoad, BSave&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Interrupt Programmierung===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ON INT0/INT1/TIMER0/TIMER1/SERIAL, RETURN, ENABLE, DISABLE, &lt;br /&gt;
COUNTERx, CAPTUREx, INTERRUPTS, CONFIG, START, LOAD.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Bit Manipulation===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SET, RESET, ROTATE, SHIFT, BITWAIT, TOGGLE.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Variablen===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DIM, BIT , BYTE , INTEGER , WORD, LONG, SINGLE, DOUBLE, STRING , DEFBIT,&lt;br /&gt;
DEFBYTE, DEFINT, DEFWORD.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sonstiges===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
REM, ' , SWAP, END, STOP, CONST, DELAY, WAIT, WAITMS, GOTO, GOSUB, &lt;br /&gt;
POWERDOWN, IDLE, DECLARE, CALL, SUB, END SUB, MAKEDEC, MAKEBCD, &lt;br /&gt;
INP,OUT, ALIAS, DIM , ERASE, DATA, READ, RESTORE, INCR, DECR, PEEK,&lt;br /&gt;
POKE, CPEEK, FUNCTION, READMAGCARD, BIN2GRAY, GRAY2BIN, CRC8, CRC16,&lt;br /&gt;
CHECKSUM.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Compiler Direktiven===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$INCLUDE, $BAUD and $CRYSTAL,  $SERIALINPUT, $SERIALOUTPUT, $RAMSIZE,&lt;br /&gt;
$RAMSTART,   $DEFAULT XRAM, $ASM-$END ASM, $LCD, $EXTERNAL, $LIB.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===String Manipulationen===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
STRING, SPACE, LEFT, RIGHT, MID, VAL, HEXVAL, LEN, STR, HEX, &lt;br /&gt;
LTRIM, RTRIM, TRIM, LCASE, UCASE, FORMAT, FUSING, INSTR. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Erläuterung grundlegender Bascom-Funktionen==&lt;br /&gt;
Hier einige kleine Programme welche die grundlegende Funktionen und Syntax zeigen. Es wird immer ein komplettes Programm gezeigt, so das man dieses einfach kopieren und austesten kann. Bascom Programme bestehen in der Regel fast immer nur aus einer Sourcecode-Datei (obwohl es auch anders geht), also nicht wie bei der Programmiersprache C üblich aus mehreren Dateien (Header-Dateien etc.). Auch das macht Bascom besonders Einsteigerfreundlich.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Das berühmte Hello World Programm in Bascom===&lt;br /&gt;
Da bei einem Controllerboard gewöhnlich kein Bildschirm zur Verfügung steht, verbindet man das Board über einfaches RS232 Kabel (nur 3 Drähte sind notwendig) mit dem PC. Ein [[Terminalprogramm]] (Bascom hat bereits eines in der Entwicklungsumgebung integriert) zeigt nun auf dem Bildschirm alle Ausgaben des Boardes (z.B. des Print Befehles) an. Eine Methode die zum Debuggen von Programmen oft genutzt wird. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $regfile = &amp;quot;m32def.dat&amp;quot; 'Die Anweisung bestimmt Ccntrollertyp, hier AVR Mega 32&lt;br /&gt;
 $framesize = 32         'Stackanweisungen, die eigentlich nur bei größeren Programmen &lt;br /&gt;
 $swstack = 32           'wirklich nötig werden&lt;br /&gt;
 $hwstack = 32&lt;br /&gt;
 $crystal = 16000000     'Die Frequenz des verwendeten Quarzes&lt;br /&gt;
&lt;br /&gt;
 $baud = 9600            'Die Baudrate für RS232 Ausgabe. &lt;br /&gt;
                         'Sie muss auch bei PC Terminalprogramm identisch sein&lt;br /&gt;
  do&lt;br /&gt;
    Print &amp;quot;**** RN-CONTROL sagt Hello World *****&amp;quot;&lt;br /&gt;
    wait 1&lt;br /&gt;
  loop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Einen I/O Port umschalten===&lt;br /&gt;
Die wohl wichtigste Aufgabe beim Controller ist die Programmierung der Ports. Also das Bestimmen des Ausgangspegels eines Controllerpins. &lt;br /&gt;
Bascom verfügt über zwei Möglichkeiten die Ports zu beeinflussen, die sogenannten High-Level Befehle und die Registerzuweisungen wie sie in C üblich sind.&lt;br /&gt;
Zuerst ein Programm mit High-Level Anweisung. Ein [[Pin]] wird als Ausgang definiert und dann fortlaufend ein- und ausgeschaltet. Ist über einen Widerstand eine LED an diesem Controllerpin angschlossen (so im Falle von [[RN-Control]]), so ergibt sich ein Blinken&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $regfile = &amp;quot;m32def.dat&amp;quot;  'Die Anweisung bestimmt Controllertyp, hier AVR Mega 32&lt;br /&gt;
 $framesize = 32          'Stackanweisungen, die eigentlich nur bei größeren Programmen &lt;br /&gt;
 $swstack = 32            'wirklich nötig werden&lt;br /&gt;
 $hwstack = 32&lt;br /&gt;
 $crystal = 16000000      'Die Frequenz des verwendeten Quarzes&lt;br /&gt;
&lt;br /&gt;
 $baud = 9600             'Die Baudrate für RS232 Ausgabe. &lt;br /&gt;
                          'Sie muss auch bei PC Terminalprogramm identisch sein&lt;br /&gt;
&lt;br /&gt;
 Config Pinc.0 = Output  'Ein Pin wird aus Ausgang konfiguriert PC0 (also Pin0 von Port C)&lt;br /&gt;
&lt;br /&gt;
  do&lt;br /&gt;
     Portc.0 = 1          'Pin wird auf High, also 5V geschaltet&lt;br /&gt;
     Waitms 100&lt;br /&gt;
     Portc.0 = 0          'Pin wird auf Low, also 0V geschaltet&lt;br /&gt;
     Waitms 100&lt;br /&gt;
  loop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man kann das ganze noch etwas übersichtlicher gestalten, indem man dem Port-[[Pin]] im Programmcode eine andere Bezeichnung gibt, z.B. LED, wie in dem folgenden Beispiel. Durch den Alias-Befehl wird das ganze Programm wesentlich klarer, so daß einige Kommentarzeilen durchaus entfallen könnten. Man sollte sich daher angewöhnen, den Alias-Befehl auch zu nutzen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $regfile = &amp;quot;m32def.dat&amp;quot;  'Die Anweisung bestimmt Controllertyp, hier AVR Mega 32&lt;br /&gt;
 $framesize = 32          'Stackanweisungen, die eigentlich nur bei größeren Programmen &lt;br /&gt;
 $swstack = 32            'wirklich nötig werden&lt;br /&gt;
 $hwstack = 32&lt;br /&gt;
 $crystal = 16000000      'Die Frequenz des verwendeten Quarzes&lt;br /&gt;
&lt;br /&gt;
 $baud = 9600             'Die Baudrate für RS232 Ausgabe. &lt;br /&gt;
                          'Sie muss auch beim PC Terminalprogramm identisch sein&lt;br /&gt;
&lt;br /&gt;
 Config Pinc.0 = Output  'Ein Pin wird als Ausgang konfiguriert PC0 (also Pin0 von Port C)&lt;br /&gt;
 Led Alias Portc.0       &lt;br /&gt;
&lt;br /&gt;
  do&lt;br /&gt;
     Led = 1              'Pin wird auf High, also 5V geschaltet&lt;br /&gt;
     Waitms 100&lt;br /&gt;
     Led = 0              'Pin wird auf Low, also 0V geschaltet&lt;br /&gt;
     Waitms 100&lt;br /&gt;
  loop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Beispiel kommt ohne den High-Level Befehl CONFIG aus, indem direkt in das Datenrichtungsregister des Controllers geschrieben wird. In diesem Register steht jedes Bit für den Betriebsmodus eines Pins. Die 1 bedeutet, der Pin ist auf Ausgang geschaltet, 0 bedeutet Eingang. Diese Schreibweise hat den Nachteil, das sie ein wenig unübersichtlicher ist, man kann sehr schnell [[Pin]]´s verwechseln. Vorteil ist allerdings, daß alle 8 [[Pin]]´s eines Portes gleichzeitig mit einer Zuweisung definiert werden können. Es gibt daher Programmierer, die diese Methode bevorzugen, insbesondere wenn Sie auch Controller in C programmieren. Das Ergebnis ist in jedem Fall gleich: ein Blinklicht&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $regfile = &amp;quot;m32def.dat&amp;quot;  'Die Anweisung bestimmt Controllertyp, hier [[AVR]] Mega 32&lt;br /&gt;
 $framesize = 32          'Stackanweisungen, die eigentlich nur bei größeren Programmen &lt;br /&gt;
 $swstack = 32            'wirklich nötig werden&lt;br /&gt;
 $hwstack = 32&lt;br /&gt;
 $crystal = 16000000      'Die Frequenz des verwendeten Quarzes&lt;br /&gt;
&lt;br /&gt;
 $baud = 9600             'Die Baudrate für RS232 Ausgabe. &lt;br /&gt;
                          'Sie muss auch bei PC Terminalprogramm identisch sein&lt;br /&gt;
&lt;br /&gt;
 DDRC = &amp;amp;b00000001        'Port PC0 wird als Ausgang definiert, man hätte hier auch&lt;br /&gt;
                          'DDRC =1 schreiben können. Man verwendet aber oft die Bitdarstellung&lt;br /&gt;
                          'um alle 8 Bit besser überschauen zu können&lt;br /&gt;
&lt;br /&gt;
  do&lt;br /&gt;
     Portc.0 = 1          'Pin wird auf High, also 5V geschaltet&lt;br /&gt;
     Waitms 100&lt;br /&gt;
     Portc.0 = 0          'Pin wird auf Low, also 0V geschaltet&lt;br /&gt;
     Waitms 100&lt;br /&gt;
  loop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===I/O-Port als Eingang===&lt;br /&gt;
Im nachfolgenden Beispiel möchten wir ein PIN als Eingang verwenden und den Zustand eines Tasters abfragen. Dazu werden die Pole eines Tastes einmal mit GND (Masse) und einmal mit einem Port PA0 verbunden. Wir hätten auch jeden anderen Port nehmen können, da beim [[Avr]] Controller nahezu alle Pin´s auch auf Eingang geschaltet werden können. Der übersichtlichkeit halber verwenden wir wieder den Config und den Alias Befehl um den Port in Taster umzutaufen.&lt;br /&gt;
Etwas gewöhnungsbedürftig ist in Bascom das man bei der Definition von Eingangsports nicht PORT sondern PIN beim Config-Befehl angibt. Eine weitere Besonderheit dieses Beispiels ist der Befehl ''Porta.0=1'' beim EIngabeport. Dieser Befehl sorgt dafür das im Controller der EIngangsport über einen hohen Widerstand (ca. 100k) mit High (5V) verbunden wird. Dadurch erreicht man, das bei unbelegtem Port, in unserem Fall ungedrückte Taste, immer ein High Signal gelesen wird. Erst wenn der Taster gedrückt wird, wird diese Spannung quasi kurzgeschlossen und so ein LOW angelegt. Das ganze Beispiel bewirkt nun das bei gedrückter Taste die LED leuchtet und beim loslassen wieder aus geht. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $regfile = &amp;quot;m32def.dat&amp;quot;  'Die Anweisung bestimmt Controllertyp, hier AVR Mega 32&lt;br /&gt;
 $framesize = 32          'Stackanweisungen, die eigentlich nur bei größeren Programmen &lt;br /&gt;
 $swstack = 32            'wirklich nötig werden&lt;br /&gt;
 $hwstack = 32&lt;br /&gt;
 $crystal = 16000000      'Die Frequenz des verwendeten Quarzes&lt;br /&gt;
&lt;br /&gt;
 $baud = 9600             'Die Baudrate für RS232 Ausgabe. &lt;br /&gt;
                          'Sie muss auch bei PC Terminalprogramm identisch sein&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Config Portc.0 = Output  'Ein Pin wird aus Ausgang konfiguriert PC0 (also Pin0 von Port C)&lt;br /&gt;
 Led Alias Portc.0       &lt;br /&gt;
 Config Pina.0 = Input    'Ein Pin (PA0) wird als Eingang definiert&lt;br /&gt;
 Taster Alias Pina.0&lt;br /&gt;
 Porta.0=1                'Interner Pullup Widerstand ein&lt;br /&gt;
 &lt;br /&gt;
  do&lt;br /&gt;
     if taster=0 then&lt;br /&gt;
       Led=1            'Pin wird auf High, also 5V geschaltet&lt;br /&gt;
     else&lt;br /&gt;
       Led = 0          'Pin wird auf Low, also 0V geschaltet&lt;br /&gt;
     endif&lt;br /&gt;
     Waitms 100&lt;br /&gt;
  loop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Tips und Tricks==&lt;br /&gt;
&lt;br /&gt;
===Wie man besonders kompakten Code zerzeugt===&lt;br /&gt;
* Beachte das 32Byte HW-Stack-Minimum für ISR. &lt;br /&gt;
* Vermeide LOCAL Variable &lt;br /&gt;
* Vermeide SUB / FUNCTION mit Parameterübergabe &lt;br /&gt;
* Vermeide Bit-Variable &lt;br /&gt;
* Vermeide a&amp;gt;b, verwende a&amp;gt;=c oder b&amp;lt;a (RISC-Prozessor kennt kein größer als) &lt;br /&gt;
* Vermeide Single/Long etc. und dazugehörige Matheoperationen (am besten nur Byte und Word)&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Bascom - Erstes Programm in den AVR Controller übertragen]]&lt;br /&gt;
* [[RN-Control]]&lt;br /&gt;
* [[RNBFRA-Board]]&lt;br /&gt;
* [[Avr]]&lt;br /&gt;
* [[RN-Board FAQ-Seite]] mit wichtigen Einstiegstips&lt;br /&gt;
* [[Sourcevergleich]] - GCC und Bascom&lt;br /&gt;
* [[Bascom State Machine Menu]] Umfangreiche Menüs in Bascom mit einer State Machine programmieren&lt;br /&gt;
* [[Bascom Inside]]&lt;br /&gt;
* [[Bascom Libraries]]&lt;br /&gt;
* [[Assembler Einführung für Bascom-User]]&lt;br /&gt;
* [[Kategorie:Quellcode Bascom]]&lt;br /&gt;
&lt;br /&gt;
==Literatur==&lt;br /&gt;
* [[Buchvorstellungen|Programmieren der AVR RISC Mikrocontroller mit BASCOM-AVR; 2. Auflage]]&lt;br /&gt;
* [[Buchvorstellungen|Bascom–AVR, Autor M.Meissner - Beschreibung der Bascom IDE]]&lt;br /&gt;
* [[Buchvorstellungen|AVR-Microcontroller Lehrbuch – Ein tieferer Einstieg in Bascom und AT-MEGA8 und ähnliche AVR-Controller]]&lt;br /&gt;
* [[Buchvorstellungen|BASCOM-AVR Sprachbefehle - Ein umfangreiches Werk welches alle Befehle beschreibt ]]&lt;br /&gt;
* [[Buchvorstellungen|Mega16 Programmierung am Beispiel des RNBFRA-Boards]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
* [http://www.mcselec.com Niederländischer Hersteller MCSELEC]&lt;br /&gt;
* [http://www.robotikhardware.de Bezugsquelle in Deutschland u.a. Robotikhardware.de] &lt;br /&gt;
* [http://www.roboternetz.de/phpBB2/viewtopic.php?t=1511 Bauanleitungen zu Experimentier- und Roboterboards]&lt;br /&gt;
* [http://www.roboternetz.de/phpBB2/dload.php?action=file&amp;amp;file_id=169 RN-Timer Windows Programm zur Timer-Berechnung]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Microcontroller]]&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:Robotikeinstieg]]&lt;br /&gt;
[[Kategorie:Praxis]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=8852</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=8852"/>
				<updated>2006-09-21T21:55:57Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: /* Bascom State Machine Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des AVR Butterfly IAR-C Code (bzw. der GCC-Portierung) nach Bascom [[http://www.roboternetz.de/phpBB2/viewtopic.php?t=23231]] entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde stark reduziert, so dass für das gesammte Programm der Menü-Rahmen mit allen Unterprogrammen übrig geblieben ist. &lt;br /&gt;
&lt;br /&gt;
== Die Menü-Struktur ==&lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
== Sample Code ==&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). &lt;br /&gt;
Auf dem Simulator-LCD-Display werden die Ebenen mit angezeigt (3 -&amp;gt; 31 -&amp;gt; 32 -&amp;gt; 321 etc.)&lt;br /&gt;
&lt;br /&gt;
Weitere Erklärungen stehen im Quelltext.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]     mit Hinweisen zur Bascom State Machine&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=8851</id>
		<title>Bascom State Machine Menu</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Bascom_State_Machine_Menu&amp;diff=8851"/>
				<updated>2006-09-21T21:48:54Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bascom State Machine Menu ==&lt;br /&gt;
&lt;br /&gt;
Beim der Umsetzung des AVR Butterfly IAR-C Code (bzw. der GCC-Portierung) nach Bascom entstand folgende hoch effiziente State Machine zur Umsetzung eines Menüs für einen Datalogger.&lt;br /&gt;
&lt;br /&gt;
Der Code wurde stark reduziert, so dass für das gesammte Programm der Menü-Rahmen mit allen Unterprogrammen übrig geblieben ist. &lt;br /&gt;
[[Bild:Butterflymenu.png]]&lt;br /&gt;
&lt;br /&gt;
Das Code-Beispiel wurde '''für den Bascom Simulator optimiert''' und kann dort direkt mit den Ziffern-Tasten bedient werden (NumLock!). Im Code stehen weitere Erklärungen.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
'Beispiel für ein State Machine Menü&lt;br /&gt;
'Das Beispiel ist für den BASCOM-Simulator angepasst worden&lt;br /&gt;
'  getestet mit BASCOM 1.11.8.1&lt;br /&gt;
'  Codelänge 2362 Byte&lt;br /&gt;
'&lt;br /&gt;
'Hinweis: Im Simulator müssen die Eingaben in das &amp;quot;Terminal Emulator Window&amp;quot; erfolgen!&lt;br /&gt;
'Auf der Tastatur ergeben sich für die VIER Joystick-Positionen folgende Umsetzungen&lt;br /&gt;
'&lt;br /&gt;
'              [Key_plus ]&lt;br /&gt;
' [Key_prev]   [         ]    [Key_next]&lt;br /&gt;
'              [Key_minus]&lt;br /&gt;
'&lt;br /&gt;
'&lt;br /&gt;
'              [ Taste_8 ]&lt;br /&gt;
' [Taste_4 ]   [         ]    [Taste_6 ]&lt;br /&gt;
'              [ Taste_2 ]&lt;br /&gt;
'&lt;br /&gt;
'bzw. im ASCII-Code&lt;br /&gt;
Const Key_null = 0                                          'keine Taste gedrückt&lt;br /&gt;
Const Key_next = 54&lt;br /&gt;
Const Key_prev = 52&lt;br /&gt;
Const Key_plus = 56&lt;br /&gt;
Const Key_minus = 50&lt;br /&gt;
&lt;br /&gt;
'los gehts:&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$framesize = 32                                             'Stack&lt;br /&gt;
$swstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
&lt;br /&gt;
'Pins des LCD-Modules setzen ggf. an eigene Anschlüsse anpassen&lt;br /&gt;
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7&lt;br /&gt;
Config Lcdmode = Port&lt;br /&gt;
Config Lcdbus = 4                                           '4 bit mode&lt;br /&gt;
Config Lcd = 20 * 4&lt;br /&gt;
Initlcd&lt;br /&gt;
Cls&lt;br /&gt;
&lt;br /&gt;
'******** state machine states ************************************************&lt;br /&gt;
' Menu state machine states&lt;br /&gt;
Const St_avrbf = 10&lt;br /&gt;
Const St_avrbf_rev = 11&lt;br /&gt;
&lt;br /&gt;
Const St_time = 20&lt;br /&gt;
Const St_time_clock = 21&lt;br /&gt;
Const St_time_clock_func = 22&lt;br /&gt;
Const St_time_clock_adjust = 23&lt;br /&gt;
Const St_time_clock_adjust_func = 24&lt;br /&gt;
Const St_time_clockformat_adjust = 25&lt;br /&gt;
Const St_time_clockformat_adjust_func = 26&lt;br /&gt;
Const St_time_date = 27&lt;br /&gt;
Const St_time_date_func = 28&lt;br /&gt;
Const St_time_date_adjust = 29&lt;br /&gt;
Const St_time_date_adjust_func = 30&lt;br /&gt;
Const St_time_dateformat_adjust = 31&lt;br /&gt;
Const St_time_dateformat_adjust_func = 32&lt;br /&gt;
&lt;br /&gt;
Const St_datalogger = 40&lt;br /&gt;
Const St_datalogger_logcycle = 41&lt;br /&gt;
Const St_datalogger_logcycle_func = 42&lt;br /&gt;
Const St_datalogger_erase = 43&lt;br /&gt;
Const St_datalogger_erase_select = 44&lt;br /&gt;
Const St_datalogger_erase_func = 45&lt;br /&gt;
Const St_datalogger_logcount = 46&lt;br /&gt;
Const St_datalogger_logcount_func = 47&lt;br /&gt;
Const St_datalogger_rs232 = 48&lt;br /&gt;
Const St_datalogger_rs232_select = 49&lt;br /&gt;
Const St_datalogger_rs232_func = 50&lt;br /&gt;
&lt;br /&gt;
Const St_adc = 60&lt;br /&gt;
Const St_temperature = 65&lt;br /&gt;
Const St_temperature_func = 66&lt;br /&gt;
Const St_voltage = 70&lt;br /&gt;
Const St_voltage_func = 71&lt;br /&gt;
Const St_adc_raw = 75&lt;br /&gt;
Const St_adc_raw_func = 76&lt;br /&gt;
Const St_adc_batt = 80&lt;br /&gt;
Const St_adc_batt_func = 81&lt;br /&gt;
&lt;br /&gt;
Const St_options = 90&lt;br /&gt;
Const St_options_display_contrast = 91&lt;br /&gt;
Const St_options_display_contrast_func = 92&lt;br /&gt;
Const St_options_power_off = 95&lt;br /&gt;
Const St_options_power_off_func = 97&lt;br /&gt;
Const St_options_auto_power_save = 100&lt;br /&gt;
Const St_options_auto_power_save_func = 101&lt;br /&gt;
Const St_options_keyclick = 105&lt;br /&gt;
Const St_options_keyclick_func = 106&lt;br /&gt;
Const St_options_boot = 110&lt;br /&gt;
Const St_options_boot_select = 111&lt;br /&gt;
Const St_options_boot_func = 112&lt;br /&gt;
&lt;br /&gt;
'********* State Variables ****************************************************&lt;br /&gt;
Dim Tab_state As Byte , Tab_input As Byte , Tab_nextstate As Byte&lt;br /&gt;
Dim Tab_text As String * 25&lt;br /&gt;
&lt;br /&gt;
Dim State As Byte , State_renew As Byte&lt;br /&gt;
&lt;br /&gt;
'Initial state variables&lt;br /&gt;
State = St_avrbf&lt;br /&gt;
State_renew = 1&lt;br /&gt;
&lt;br /&gt;
'******** Joystick/Key Settings ***********************************************&lt;br /&gt;
Dim Key As Byte                                             'key in Mainloop&lt;br /&gt;
&lt;br /&gt;
'******** LCD *****************************************************************&lt;br /&gt;
Dim Lcd_textbuffer As String * 25&lt;br /&gt;
&lt;br /&gt;
'******** allg Variablen ******************************************************&lt;br /&gt;
Dim I As Byte , J As Byte&lt;br /&gt;
Dim I1 As Byte&lt;br /&gt;
Dim W As Word , W1 As Word&lt;br /&gt;
&lt;br /&gt;
'********** MAIN-Loop *********************************************************&lt;br /&gt;
Do&lt;br /&gt;
   'Tastaturabfrage&lt;br /&gt;
    Key = Inkey()&lt;br /&gt;
&lt;br /&gt;
   'Menüeintrag und Tastencodes finden&lt;br /&gt;
   If Key &amp;lt;&amp;gt; Key_null Then                                  'save power&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore State_machine&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_input&lt;br /&gt;
         Read Tab_nextstate&lt;br /&gt;
         If State = Tab_state Then&lt;br /&gt;
            If Key = Tab_input Then&lt;br /&gt;
               State = Tab_nextstate&lt;br /&gt;
               State_renew = 1                              'LCD refresh&lt;br /&gt;
               Key = Key_null                               'reset key status after get a new state of state machine (prevent influence on GOSUBs)&lt;br /&gt;
            End If&lt;br /&gt;
         End If&lt;br /&gt;
      Loop Until State_renew = 1 Or Tab_state = 255&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
   'Endlosschleife mit Unterprogrammen&lt;br /&gt;
   Select Case State                                        '320 Byte für 20 Gosub&lt;br /&gt;
      Case St_time_clock_func : Gosub Showclock&lt;br /&gt;
      Case St_time_clock_adjust_func : Gosub Setclock&lt;br /&gt;
      Case St_time_clockformat_adjust_func : Gosub Setclockformat&lt;br /&gt;
      Case St_time_date_func : Gosub Showdate&lt;br /&gt;
      Case St_time_date_adjust_func : Gosub Setdate&lt;br /&gt;
      Case St_time_dateformat_adjust_func : Gosub Setdateformat&lt;br /&gt;
&lt;br /&gt;
      Case St_datalogger_logcycle_func : Gosub Datalogger_setloginterval&lt;br /&gt;
      Case St_datalogger_erase_func : Gosub Datalogger_erase&lt;br /&gt;
      Case St_datalogger_logcount_func : Gosub Datalogger_logcount&lt;br /&gt;
      Case St_datalogger_rs232_func : Gosub Datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Case St_temperature_func : Gosub Temperaturefunc&lt;br /&gt;
      Case St_voltage_func : Gosub Voltagefunc&lt;br /&gt;
      Case St_adc_raw_func : Gosub Adc_raw_func&lt;br /&gt;
      Case St_adc_batt_func : Gosub Adc_batt_func&lt;br /&gt;
&lt;br /&gt;
      Case St_options_display_contrast_func : Gosub Setcontrast&lt;br /&gt;
      Case St_options_boot_func : Gosub Bootfunc&lt;br /&gt;
      Case St_options_power_off_func : Gosub Power_off_func&lt;br /&gt;
      Case St_options_auto_power_save_func : Gosub Autopower&lt;br /&gt;
      Case St_options_keyclick_func : Gosub Keyclick_set&lt;br /&gt;
   End Select&lt;br /&gt;
&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
   'place for your own code in main loop&lt;br /&gt;
   '---------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
   'LCD refresh wenn Menü verändert&lt;br /&gt;
   If State_renew = 1 Then&lt;br /&gt;
      State_renew = 0&lt;br /&gt;
      Restore Menu_text_data&lt;br /&gt;
      Do&lt;br /&gt;
         Read Tab_state&lt;br /&gt;
         Read Tab_text&lt;br /&gt;
         If State = Tab_state Then Lcd_textbuffer = Tab_text&lt;br /&gt;
      Loop Until Tab_state = 255&lt;br /&gt;
      Gosub Lcd_print&lt;br /&gt;
   End If&lt;br /&gt;
&lt;br /&gt;
 Loop&lt;br /&gt;
End&lt;br /&gt;
&lt;br /&gt;
'********* LCD SUB routines ***************************************************&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
'Subroutine: Lcd_print&lt;br /&gt;
'Call from:  anywhere&lt;br /&gt;
'Purpose:    gibt Lcd_textbuffer auf dem LCD-Display aus&lt;br /&gt;
'Result:     LCD&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Lcd_print:                                                  'Print lcd_textbuffer&lt;br /&gt;
    Cls&lt;br /&gt;
    Lcd Lcd_textbuffer&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB Clock routines**************************************************&lt;br /&gt;
Showclock:&lt;br /&gt;
  'Show the clock on the LCD&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH:MM:SS&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclock:&lt;br /&gt;
  'Adjusts the Clock&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;HH=11&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setclockformat:&lt;br /&gt;
  'Adjusts the Clockformat (12H or 24H)&lt;br /&gt;
  Lcd_textbuffer = &amp;quot;12H / 24H&amp;quot;&lt;br /&gt;
  Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* SUB date routines **************************************************&lt;br /&gt;
&lt;br /&gt;
Showdate:&lt;br /&gt;
   'Show the date on the LCD&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DD.MM.YY&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Setdate:&lt;br /&gt;
   'Adjusts the Date&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;Month=12&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'---------------------------------------------------------------&lt;br /&gt;
Setdateformat:&lt;br /&gt;
   'Adjusts the Dateformat &amp;quot;DDMMYY&amp;quot; , &amp;quot;MMDDYY&amp;quot; , &amp;quot;YYMMDD&amp;quot;&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;DDMMYY/YYMMDD&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'********* Datalogger routines ************************************************&lt;br /&gt;
Datalogger_setloginterval:&lt;br /&gt;
   'set the datalog intervall HOUR:MINUTES&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;HH:MM&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_erase:&lt;br /&gt;
  'erase the dataflash&lt;br /&gt;
  State = St_datalogger_erase&lt;br /&gt;
  State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Datalogger_logcount:&lt;br /&gt;
  'Show DF_LogCount&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;1234&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datalogger_rs232:&lt;br /&gt;
   'Print all DataLogs to RS232&lt;br /&gt;
   State = St_datalogger_rs232                              'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Temperaturefunc:&lt;br /&gt;
   'temperature measurement in °C&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;+24C&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Voltagefunc:&lt;br /&gt;
 'voltage measurement mV&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_raw_func:&lt;br /&gt;
  'ADC Temperature/Voltage/Light result as RAW&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;CH:RAW&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adc_batt_func:&lt;br /&gt;
  'battery voltage measurement&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;2900mV&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'********* Sub MENU / OPTIONS  ********************************&lt;br /&gt;
Setcontrast:&lt;br /&gt;
   'Adjust the LCD contrast&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;0...15&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bootfunc:&lt;br /&gt;
   State = St_avrbf                                         'next status of state machine&lt;br /&gt;
   State_renew = 1&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
Power_off_func:&lt;br /&gt;
  'LCD OFF&lt;br /&gt;
  State = St_options_power_off&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Autopower:&lt;br /&gt;
   'Enable/Disable auto power save&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Keyclick_set:&lt;br /&gt;
    'Enable/Disable keyclick&lt;br /&gt;
   Lcd_textbuffer = &amp;quot;ON/OFF&amp;quot;&lt;br /&gt;
   Gosub Lcd_print&lt;br /&gt;
Return&lt;br /&gt;
&lt;br /&gt;
'*********** State Machine ***************************************************&lt;br /&gt;
'112 Datensätze der State Machine ca. 350 Byte&lt;br /&gt;
'State with xxx_func forward to GOSUB xxx in main loop&lt;br /&gt;
'&lt;br /&gt;
'             [Key_plus ]&lt;br /&gt;
'[Key_prev]   [Key_enter]    [Key_next]&lt;br /&gt;
'             [Key_minus]&lt;br /&gt;
&lt;br /&gt;
State_machine:&lt;br /&gt;
'  CURRENT_STATE   INPUT    NEXT_STATE&lt;br /&gt;
Data St_avrbf , Key_plus , St_options&lt;br /&gt;
Data St_avrbf , Key_next , St_avrbf_rev&lt;br /&gt;
Data St_avrbf , Key_minus , St_time&lt;br /&gt;
&lt;br /&gt;
   Data St_avrbf_rev , Key_prev , St_avrbf&lt;br /&gt;
&lt;br /&gt;
'Date+Time-----------------------------------------------------------------&lt;br /&gt;
Data St_time , Key_plus , St_avrbf&lt;br /&gt;
Data St_time , Key_next , St_time_clock&lt;br /&gt;
Data St_time , Key_prev , St_avrbf&lt;br /&gt;
Data St_time , Key_minus , St_datalogger&lt;br /&gt;
&lt;br /&gt;
   Data St_time_clock , Key_plus , St_time_date&lt;br /&gt;
   Data St_time_clock , Key_next , St_time_clock_func&lt;br /&gt;
   Data St_time_clock , Key_prev , St_time&lt;br /&gt;
   Data St_time_clock , Key_minus , St_time_date&lt;br /&gt;
&lt;br /&gt;
      Data St_time_clock_func , Key_prev , St_time_clock&lt;br /&gt;
      Data St_time_clock_func , Key_next , St_time_clock_adjust&lt;br /&gt;
      Data St_time_clock_func , Key_minus , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clock_adjust , Key_plus , St_time_clockformat_adjust&lt;br /&gt;
         Data St_time_clock_adjust , Key_next , St_time_clock_adjust_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clock_adjust , Key_minus , St_time_clockformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clock_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_plus , St_time_clock_adjust&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_next , St_time_clockformat_adjust_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_prev , St_time_clock_func&lt;br /&gt;
         Data St_time_clockformat_adjust , Key_minus , St_time_clock_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_clockformat_adjust_func , Key_prev , St_time_clock_func&lt;br /&gt;
&lt;br /&gt;
   Data St_time_date , Key_plus , St_time_clock&lt;br /&gt;
   Data St_time_date , Key_next , St_time_date_func&lt;br /&gt;
   Data St_time_date , Key_prev , St_time&lt;br /&gt;
   Data St_time_date , Key_minus , St_time_clock&lt;br /&gt;
&lt;br /&gt;
      Data St_time_date_func , Key_plus , St_time_clock_func&lt;br /&gt;
      Data St_time_date_func , Key_prev , St_time_date&lt;br /&gt;
      Data St_time_date_func , Key_next , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
         Data St_time_date_adjust , Key_plus , St_time_dateformat_adjust&lt;br /&gt;
         Data St_time_date_adjust , Key_next , St_time_date_adjust_func&lt;br /&gt;
         Data St_time_date_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_date_adjust , Key_minus , St_time_dateformat_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_date_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_plus , St_time_date_adjust&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_next , St_time_dateformat_adjust_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_prev , St_time_date_func&lt;br /&gt;
         Data St_time_dateformat_adjust , Key_minus , St_time_date_adjust&lt;br /&gt;
&lt;br /&gt;
            Data St_time_dateformat_adjust_func , Key_prev , St_time_date_func&lt;br /&gt;
&lt;br /&gt;
'Data Logger---------------------------------------------------------------&lt;br /&gt;
Data St_datalogger , Key_plus , St_time&lt;br /&gt;
Data St_datalogger , Key_next , St_datalogger_logcycle&lt;br /&gt;
Data St_datalogger , Key_prev , St_avrbf&lt;br /&gt;
Data St_datalogger , Key_minus , St_adc&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcycle , Key_plus , St_datalogger_rs232&lt;br /&gt;
   Data St_datalogger_logcycle , Key_next , St_datalogger_logcycle_func&lt;br /&gt;
   Data St_datalogger_logcycle , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcycle , Key_minus , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcycle_func , Key_prev , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_erase , Key_plus , St_datalogger_logcycle&lt;br /&gt;
   Data St_datalogger_erase , Key_next , St_datalogger_erase_select&lt;br /&gt;
   Data St_datalogger_erase , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_erase , Key_minus , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_erase_select , Key_next , St_datalogger_erase_func&lt;br /&gt;
      Data St_datalogger_erase_select , Key_prev , St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_erase_func  -&amp;gt; new State = St_datalogger_erase&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_logcount , Key_plus , St_datalogger_erase&lt;br /&gt;
   Data St_datalogger_logcount , Key_next , St_datalogger_logcount_func&lt;br /&gt;
   Data St_datalogger_logcount , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_logcount , Key_minus , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_logcount_func , Key_prev , St_datalogger_logcount&lt;br /&gt;
&lt;br /&gt;
   Data St_datalogger_rs232 , Key_plus , St_datalogger_logcount&lt;br /&gt;
   Data St_datalogger_rs232 , Key_next , St_datalogger_rs232_select&lt;br /&gt;
   Data St_datalogger_rs232 , Key_prev , St_datalogger&lt;br /&gt;
   Data St_datalogger_rs232 , Key_minus , St_datalogger_logcycle&lt;br /&gt;
&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_next , St_datalogger_rs232_func&lt;br /&gt;
      Data St_datalogger_rs232_select , Key_prev , St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
         'SUB St_datalogger_rs232_func -&amp;gt; new State = St_datalogger_rs232&lt;br /&gt;
&lt;br /&gt;
'ADC-----------------------------------------------------------------------&lt;br /&gt;
Data St_adc , Key_plus , St_datalogger&lt;br /&gt;
Data St_adc , Key_next , St_temperature&lt;br /&gt;
Data St_adc , Key_prev , St_avrbf&lt;br /&gt;
Data St_adc , Key_minus , St_options&lt;br /&gt;
&lt;br /&gt;
   Data St_temperature , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_temperature , Key_next , St_temperature_func&lt;br /&gt;
   Data St_temperature , Key_prev , St_avrbf&lt;br /&gt;
   Data St_temperature , Key_minus , St_voltage&lt;br /&gt;
&lt;br /&gt;
      Data St_temperature_func , Key_prev , St_temperature&lt;br /&gt;
&lt;br /&gt;
   Data St_voltage , Key_plus , St_temperature&lt;br /&gt;
   Data St_voltage , Key_next , St_voltage_func&lt;br /&gt;
   Data St_voltage , Key_prev , St_avrbf&lt;br /&gt;
   Data St_voltage , Key_minus , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
      Data St_voltage_func , Key_prev , St_voltage&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_raw , Key_plus , St_voltage&lt;br /&gt;
   Data St_adc_raw , Key_next , St_adc_raw_func&lt;br /&gt;
   Data St_adc_raw , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_raw , Key_minus , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_raw_func , Key_prev , St_adc_raw&lt;br /&gt;
&lt;br /&gt;
   Data St_adc_batt , Key_plus , St_adc_raw&lt;br /&gt;
   Data St_adc_batt , Key_next , St_adc_batt_func&lt;br /&gt;
   Data St_adc_batt , Key_prev , St_avrbf&lt;br /&gt;
   Data St_adc_batt , Key_minus , St_temperature&lt;br /&gt;
&lt;br /&gt;
      Data St_adc_batt_func , Key_prev , St_adc_batt&lt;br /&gt;
&lt;br /&gt;
'Options-------------------------------------------------------------------&lt;br /&gt;
Data St_options , Key_plus , St_adc&lt;br /&gt;
Data St_options , Key_next , St_options_display_contrast&lt;br /&gt;
Data St_options , Key_prev , St_avrbf&lt;br /&gt;
Data St_options , Key_minus , St_avrbf&lt;br /&gt;
&lt;br /&gt;
   Data St_options_display_contrast , Key_plus , St_options_keyclick&lt;br /&gt;
   Data St_options_display_contrast , Key_next , St_options_display_contrast_func&lt;br /&gt;
   Data St_options_display_contrast , Key_prev , St_options&lt;br /&gt;
   Data St_options_display_contrast , Key_minus , St_options_boot&lt;br /&gt;
&lt;br /&gt;
      Data St_options_display_contrast_func , Key_prev , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
   Data St_options_boot , Key_plus , St_options_display_contrast&lt;br /&gt;
   Data St_options_boot , Key_next , St_options_boot_select&lt;br /&gt;
   Data St_options_boot , Key_prev , St_options&lt;br /&gt;
   Data St_options_boot , Key_minus , St_options_power_off&lt;br /&gt;
&lt;br /&gt;
      Data St_options_boot_select , Key_next , St_options_boot_func&lt;br /&gt;
      Data St_options_boot_select , Key_prev , St_options_boot&lt;br /&gt;
&lt;br /&gt;
   Data St_options_power_off , Key_plus , St_options_boot&lt;br /&gt;
   Data St_options_power_off , Key_next , St_options_power_off_func&lt;br /&gt;
   Data St_options_power_off , Key_prev , St_options&lt;br /&gt;
   Data St_options_power_off , Key_minus , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
      'SUB St_options_power_off_func -&amp;gt; new State = St_options_power_off&lt;br /&gt;
&lt;br /&gt;
   Data St_options_auto_power_save , Key_plus , St_options_power_off&lt;br /&gt;
   Data St_options_auto_power_save , Key_next , St_options_auto_power_save_func&lt;br /&gt;
   Data St_options_auto_power_save , Key_prev , St_options&lt;br /&gt;
   Data St_options_auto_power_save , Key_minus , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
      Data St_options_auto_power_save_func , Key_prev , St_options_auto_power_save&lt;br /&gt;
&lt;br /&gt;
   Data St_options_keyclick , Key_plus , St_options_auto_power_save&lt;br /&gt;
   Data St_options_keyclick , Key_next , St_options_keyclick_func&lt;br /&gt;
   Data St_options_keyclick , Key_prev , St_options&lt;br /&gt;
   Data St_options_keyclick , Key_minus , St_options_display_contrast&lt;br /&gt;
&lt;br /&gt;
      Data St_options_keyclick_func , Key_prev , St_options_keyclick&lt;br /&gt;
&lt;br /&gt;
'Stop Condition&lt;br /&gt;
Data 255 , 255 , 255&lt;br /&gt;
&lt;br /&gt;
'************ menu text strings: max. length 24 Byte!! ***********************&lt;br /&gt;
Menu_text_data:&lt;br /&gt;
Data St_avrbf , &amp;quot;1 Butterfly Bascom&amp;quot;&lt;br /&gt;
   Data St_avrbf_rev , &amp;quot;11 Rev 1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_time , &amp;quot;2 Time&amp;quot;&lt;br /&gt;
   Data St_time_clock , &amp;quot;21 Clock&amp;quot;&lt;br /&gt;
      Data St_time_clock_adjust , &amp;quot;211 Adjust Clock&amp;quot;&lt;br /&gt;
      Data St_time_clockformat_adjust , &amp;quot;212 Clock Format&amp;quot;&lt;br /&gt;
   Data St_time_date , &amp;quot;22 Date&amp;quot;&lt;br /&gt;
      Data St_time_date_adjust , &amp;quot;221 Adjust Date&amp;quot;&lt;br /&gt;
      Data St_time_dateformat_adjust , &amp;quot;222 Date Format&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_datalogger , &amp;quot;3 DataLogger&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcycle , &amp;quot;31 Log Cycle&amp;quot;&lt;br /&gt;
   Data St_datalogger_erase , &amp;quot;32 Delete Flash&amp;quot;&lt;br /&gt;
      Data St_datalogger_erase_select , &amp;quot;321 RIGHT Delete DF&amp;quot;&lt;br /&gt;
   Data St_datalogger_logcount , &amp;quot;33 Show LogCount&amp;quot;&lt;br /&gt;
   Data St_datalogger_rs232 , &amp;quot;34 Print to RS232&amp;quot;&lt;br /&gt;
      Data St_datalogger_rs232_select , &amp;quot;341 RIGHT Print9600B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_adc , &amp;quot;4 ADC&amp;quot;&lt;br /&gt;
   Data St_temperature , &amp;quot;41 Temperature&amp;quot;&lt;br /&gt;
   Data St_voltage , &amp;quot;42 Voltage&amp;quot;&lt;br /&gt;
   Data St_adc_raw , &amp;quot;43 ADC Port RAW&amp;quot;&lt;br /&gt;
   Data St_adc_batt , &amp;quot;44 Battery&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Data St_options , &amp;quot;5 Options&amp;quot;&lt;br /&gt;
   Data St_options_display_contrast , &amp;quot;51 LCD contrast&amp;quot;&lt;br /&gt;
   Data St_options_boot , &amp;quot;52 Bootloader&amp;quot;&lt;br /&gt;
      Data St_options_boot_select , &amp;quot;521 RIGHT bootloader&amp;quot;&lt;br /&gt;
   Data St_options_power_off , &amp;quot;53 LCD OFF&amp;quot;&lt;br /&gt;
   Data St_options_auto_power_save , &amp;quot;54 LCD Auto Power&amp;quot;&lt;br /&gt;
   Data St_options_keyclick , &amp;quot;55 Key Click&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Data 255 , &amp;quot;&amp;quot;                                               'Stop Condition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Sourcevergleich]]     mit Hinweisen zur Bascom State Machine&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Butterflymenu.png&amp;diff=8850</id>
		<title>Datei:Butterflymenu.png</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Butterflymenu.png&amp;diff=8850"/>
				<updated>2006-09-21T21:38:10Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Butterflymenu.gif&amp;diff=8849</id>
		<title>Datei:Butterflymenu.gif</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Butterflymenu.gif&amp;diff=8849"/>
				<updated>2006-09-21T21:35:53Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: Datenlogger Menü für AVR Butterfly&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Datenlogger Menü für AVR Butterfly&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Sourcevergleich&amp;diff=8848</id>
		<title>Sourcevergleich</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Sourcevergleich&amp;diff=8848"/>
				<updated>2006-09-21T21:28:01Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Es sollen hier einige Beispiele gebracht werden, wie einige Dinge in verschiedenen Sprachen gelöst werden können. Es sollte nicht als Wettbewerb der Compiler gesehen werden, welcher denn nun der &amp;quot;Bessere&amp;quot; sei. &lt;br /&gt;
Über Vorzüge und Nachteile der verschiedenen Sprachen gibt es durchaus kontroversielle Ansichten. Daran will ich hier gar nicht rütteln, über Geschmack kann man nicht streiten. &lt;br /&gt;
&lt;br /&gt;
=Hello, world=&lt;br /&gt;
&lt;br /&gt;
Ein ganz einfaches Programm, oft auch das erste, ist, den Text &amp;quot;Hello, world !&amp;quot; auf dem Terminal erscheinen zu lassen. &lt;br /&gt;
&lt;br /&gt;
Was so trivial klingt, hat den Zweck, mehrere Dinge zu überprüfen:&lt;br /&gt;
* Komme ich mit der Entwicklungsoberfläche zurecht&lt;br /&gt;
* funktioniert das Kompilieren und das Übertragen eines Programms auf den Microcontroller&lt;br /&gt;
* stimmen die Einstellungen Quarz und Baudrate&lt;br /&gt;
* funktioniert die RS232-Verbindung mit dem PC&lt;br /&gt;
&lt;br /&gt;
 Natürlich ist der Text vollkommen egal, er hat sich ganz einfach eingebürgert und fast jeder &lt;br /&gt;
 weiß, was damit gemeint ist.&lt;br /&gt;
&lt;br /&gt;
==BasCom (Hello, world)==&lt;br /&gt;
Die folgenden vier Zeilen lassen ahnen, warum Bascom speziell für Einsteiger geradezu ein Segen ist:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $Crystal=8000000&lt;br /&gt;
 $Baud=9600&lt;br /&gt;
&lt;br /&gt;
 Print &amp;quot;Hello, world !&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GCC (Hello, world)==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
#define F_CPU 			8000000 &lt;br /&gt;
#define USART_BAUD_RATE 	9600 &lt;br /&gt;
#define USART_BAUD_SELECT 	(F_CPU/(USART_BAUD_RATE*16L)-1) &lt;br /&gt;
&lt;br /&gt;
//-----------------------------------------------------&lt;br /&gt;
void _writeString (const char *string) &lt;br /&gt;
{ &lt;br /&gt;
     while (*string) &lt;br /&gt;
     {&lt;br /&gt;
         while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))&lt;br /&gt;
         {} &lt;br /&gt;
&lt;br /&gt;
         UDR = *string++; &lt;br /&gt;
     }&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
//-----------------------------------------------------&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
     UCSRB = (1&amp;lt;&amp;lt;TXEN); &lt;br /&gt;
     UCSRC = (1&amp;lt;&amp;lt;URSEL) | (1&amp;lt;&amp;lt;UCSZ1) | (1&amp;lt;&amp;lt;UCSZ0); &lt;br /&gt;
     UBRRL = (unsigned char) USART_BAUD_SELECT; &lt;br /&gt;
&lt;br /&gt;
     _writeString (&amp;quot;Hallo, Welt!\n&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
     // Endlossschleife nach Verlassen von main&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn jemanden der Aufwand bei GCC erschrecken sollte:&lt;br /&gt;
&lt;br /&gt;
GCC hat keine Standard-Annahme darüber, wie und wo eigentlich der Output stattfinden sollte. Es ist für ihn nicht selbstverständlich, die UART zu verwenden. Dadurch muß natürlich mehr definiert werden.  Dem BasCom kommt zugute, daß er die ganze Konfiguration mit AVR, RS232 und PC-Terminal erstmal als gegeben nimmt.&lt;br /&gt;
&lt;br /&gt;
==AVRStudio Assembler (Hello, world)==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 .NOLIST                    ; List-Output unterdrücken&lt;br /&gt;
 .INCLUDE &amp;lt;m32def.inc&amp;gt;       ; das gibt es für jeden Controllertyp&lt;br /&gt;
 .LIST                      ; List-Output wieder aufdrehen&lt;br /&gt;
 .CSEG                      ; was nun folgt, gehört in den FLASH-Speicher&lt;br /&gt;
&lt;br /&gt;
 #define F_CPU  		8000000 &lt;br /&gt;
 #define USART_BAUD_RATE 	9600 &lt;br /&gt;
&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;     Start Adresse 0000&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 RESET:&lt;br /&gt;
     jmp INIT           ; springen nach &amp;quot;INIT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;     ISR VECTORS&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;    .....    hier kommen dann die Sprungadressen für die Interrupts rein&lt;br /&gt;
 ;             brauchen wir aber jetzt nicht&lt;br /&gt;
 .ORG INT_VECTORS_SIZE    ; dadurch haben wir aber trotzdem für die Vektoren Platz gelassen&lt;br /&gt;
 INIT:  &lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;     INITIALIZE&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
     ldi r24,high(RAMEND)     ; Stack Pointer setzen &lt;br /&gt;
     out SPH,r24              ; &amp;quot;RAMEND&amp;quot; ist in m32def.inc (s.o.) festgelegt&lt;br /&gt;
     ldi r24,low(RAMEND)      ; &lt;br /&gt;
     out SPL,r24              ;&lt;br /&gt;
 &lt;br /&gt;
        ldi	r24, (F_CPU/(USART_BAUD_RATE * 16)-1) &lt;br /&gt;
	out	UBRRL,r24&lt;br /&gt;
	ldi	r24, (1&amp;lt;&amp;lt;URSEL)|(1&amp;lt;&amp;lt;UCSZ1)|(1&amp;lt;&amp;lt;UCSZ0)&lt;br /&gt;
	out	UCSRC,r24&lt;br /&gt;
	ldi	r24,(1&amp;lt;&amp;lt;TXEN) |(1&amp;lt;&amp;lt;RXEN)&lt;br /&gt;
	out	UCSRB,r24&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;   HAUPTSCHLEIFE&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 Hauptschleife: &lt;br /&gt;
	ldi	ZL,low(Hello &amp;lt;&amp;lt; 1)	; ZL:ZH ASCIZ String addr&lt;br /&gt;
	ldi	ZH,high(Hello &amp;lt;&amp;lt; 1)	; ZL:ZH ASCIZ String addr&lt;br /&gt;
	call	printflash&lt;br /&gt;
	call	PrintCrLf&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;   ENDE&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 Ende:  &lt;br /&gt;
        rjmp Ende&lt;br /&gt;
&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;   PRINT String from Flash&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
Printflash:&lt;br /&gt;
	call	GetFlash	; get byte to R24&lt;br /&gt;
	breq	PrintXit	; zero  (string end) exit&lt;br /&gt;
	rcall	PrintR24	; send byte&lt;br /&gt;
	rjmp	Printflash	; loop&lt;br /&gt;
PrintXit:&lt;br /&gt;
	ret&lt;br /&gt;
; -----------------------------------------------------------------------&lt;br /&gt;
;  PRINT  CRLF&lt;br /&gt;
; -----------------------------------------------------------------------&lt;br /&gt;
PrintCrLf:&lt;br /&gt;
	ldi	r24,0x0D&lt;br /&gt;
	rcall	PrintR24&lt;br /&gt;
	ldi	r24,0x0A&lt;br /&gt;
; -----------------------------------------------------------------------&lt;br /&gt;
;  PRINT  R0&lt;br /&gt;
; -----------------------------------------------------------------------&lt;br /&gt;
PrintR24:&lt;br /&gt;
	sbis	UCSRA,UDRE&lt;br /&gt;
	rjmp	PrintR24&lt;br /&gt;
	out	UDR,r24&lt;br /&gt;
	ret&lt;br /&gt;
; -----------------------------------------------------------------------&lt;br /&gt;
;  Get one Flash Char&lt;br /&gt;
; -----------------------------------------------------------------------&lt;br /&gt;
GetFlash:&lt;br /&gt;
	lpm&lt;br /&gt;
	adiw	ZL,0x0001&lt;br /&gt;
	mov	r24, r0&lt;br /&gt;
	and	r24, r24&lt;br /&gt;
	ret&lt;br /&gt;
Hello:&lt;br /&gt;
	.db &amp;quot;Hello, world !&amp;quot;, 0x00 , 0x00 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=Tastatur-Echo=&lt;br /&gt;
&lt;br /&gt;
Wenn das vorhergegangene Beispiel funktioniert hat, wird man natürlich auch überprüfen wollen, ob auch die umgekehrte Richtung VOM Terminal klappt. Die einfachste &lt;br /&gt;
Methode ist es, ganz einfach jedes Zeichen, das eingegeben wird, sofort zurückzusenden. &lt;br /&gt;
&lt;br /&gt;
==BasCom (Tastatur-Echo)==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $Crystal=8000000&lt;br /&gt;
 $Baud=9600&lt;br /&gt;
&lt;br /&gt;
 DIM Zeichen as Byte&lt;br /&gt;
&lt;br /&gt;
 Do&lt;br /&gt;
   inputbin Zeichen&lt;br /&gt;
   Printbin Zeichen&lt;br /&gt;
 Loop&lt;br /&gt;
 End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GCC (Tastatur-Echo)==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
#define F_CPU 			8000000 &lt;br /&gt;
#define USART_BAUD_RATE 	9600 &lt;br /&gt;
#define USART_BAUD_SELECT 	(F_CPU/(USART_BAUD_RATE*16L)-1) &lt;br /&gt;
&lt;br /&gt;
//-----------------------------------------------------&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
	char bZeichen;&lt;br /&gt;
	&lt;br /&gt;
	UCSRB = (1 &amp;lt;&amp;lt; RXEN) | (1 &amp;lt;&amp;lt; TXEN); &lt;br /&gt;
	UCSRC = (1 &amp;lt;&amp;lt; URSEL) | (1 &amp;lt;&amp;lt; UCSZ1) | (1 &amp;lt;&amp;lt; UCSZ0); &lt;br /&gt;
	UBRRL = (unsigned char) USART_BAUD_SELECT; &lt;br /&gt;
	&lt;br /&gt;
	while (1) &lt;br /&gt;
	{ &lt;br /&gt;
		while ( !(UCSRA &amp;amp; (1 &amp;lt;&amp;lt; RXC)) ) &lt;br /&gt;
		{}&lt;br /&gt;
		bZeichen = UDR; &lt;br /&gt;
		&lt;br /&gt;
		while (!(UCSRA &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))  &lt;br /&gt;
		{} &lt;br /&gt;
		UDR = bZeichen; &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==AVRStudio Assembler (Tastatur-Echo)==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 .NOLIST                    ; List-Output unterdrücken&lt;br /&gt;
 .INCLUDE &amp;lt;m32def.inc&amp;gt;       ; das gibt es für jeden Controllertyp&lt;br /&gt;
 .LIST                      ; List-Output wieder aufdrehen&lt;br /&gt;
 .CSEG                      ; was nun folgt, gehört in den FLASH-Speicher&lt;br /&gt;
&lt;br /&gt;
 #define F_CPU  			8000000 &lt;br /&gt;
 #define USART_BAUD_RATE 	9600 &lt;br /&gt;
&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;     Start Adresse 0000&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 RESET:&lt;br /&gt;
     jmp INIT           ; springen nach &amp;quot;INIT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 .ORG INT_VECTORS_SIZE    ; dadurch haben wir für die Vektoren Platz gelassen&lt;br /&gt;
 INIT:  &lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;     INITIALIZE&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
     ldi r24,high(RAMEND)     ;Stack Pointer setzen &lt;br /&gt;
     out SPH,r24              ; &amp;quot;RAMEND&amp;quot; ist in m32def.inc (s.o.) festgelegt&lt;br /&gt;
     ldi r24,low(RAMEND)      ; &lt;br /&gt;
     out SPL,r24              ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	ldi	r24, (F_CPU/(USART_BAUD_RATE * 16)-1) &lt;br /&gt;
	out	UBRRL,r24&lt;br /&gt;
	ldi	r24, (1&amp;lt;&amp;lt;URSEL)|(1&amp;lt;&amp;lt;UCSZ1)|(1&amp;lt;&amp;lt;UCSZ0)&lt;br /&gt;
	out	UCSRC,r24&lt;br /&gt;
	ldi	r24,(1&amp;lt;&amp;lt;TXEN) |(1&amp;lt;&amp;lt;RXEN)&lt;br /&gt;
	out	UCSRB,r24&lt;br /&gt;
 &lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;   HAUPTSCHLEIFE&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 Hauptschleife: &lt;br /&gt;
	sbis	UCSRA,RXC&lt;br /&gt;
	rjmp	Hauptschleife&lt;br /&gt;
        in      r24, UDR&lt;br /&gt;
 WaitUDR:&lt;br /&gt;
	sbis	UCSRA,UDRE&lt;br /&gt;
	rjmp	WaitUDR&lt;br /&gt;
	out	UDR,r24&lt;br /&gt;
    rjmp 	Hauptschleife         ; immer wiederholen &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Bemerkungen==&lt;br /&gt;
&lt;br /&gt;
Hier ist der Unterschied des Codes nicht mehr so groß. Ganz klar, hier konnte BasCom wegen der Aufgabenstellung nicht auf eine vorgefertigte komplexe Funktion zurückgreifen.&amp;lt;br&amp;gt;&lt;br /&gt;
Bei Assembler und GCC fallen große Ähnlichkeiten auf, von den Befehlscodes mal abgesehen. &lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=Signallänge messen=&lt;br /&gt;
&lt;br /&gt;
Sehr oft ist es bei Controllern nötig, die Länge eines kurzen Impulses zu messen. Zum Beispiel, wenn man einen RC-Empfänger (Modellbauempfänger) an ein Controllerboard anschließt. Ein RC-Empfänger sendet alle 20 Millisekunden ein High-Impuls von 1 bis 2 Millisekunden Länge aus. Die Länge dieses Impules bestimmt die Position des Steuerknüppels. Man braucht also nur diese Impulsdauer zu messen, um ein Fernsteuersignal auszuwerten.&lt;br /&gt;
&lt;br /&gt;
Zur Zeitmessung selbst gibt es prinzipiell mehrere Möglichkeiten, etwa unter Verwendung eines Timers oder indem man eine bestimmte Ahnzahl von Maschinenzyklen wartet, und so eine Zeitbasis erhält. Man sollte aber nicht dem Irrtum aufsitzen, der zweite Ansatz belege keine Systemressourcen: der Ansatz schliesst die Verwendung von Interrupts, welche evtl. für andere Zwecke aktiv sein müssen, aus bzw. es werden falsche Ergebnisse geliefert. Zudem &amp;quot;hängt&amp;quot; die Applikation während des Messvorgangs.&lt;br /&gt;
&lt;br /&gt;
==BasCom (Signallänge messen)==&lt;br /&gt;
Bascom verfügt für diesen Zweck über den PULSEIN-Befehl, daher wird ein Programm extrem kurz.  Die Messung erfolgt in etwa in 100 Abstufungen, da Pulsein in 10 µSek Schritten die Zeit ermittelt (läßt sich in Libary ändern)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $regfile = &amp;quot;m32def.dat&amp;quot;   &lt;br /&gt;
 $framesize = 32&lt;br /&gt;
 $swstack = 32&lt;br /&gt;
 $hwstack = 32&lt;br /&gt;
 $crystal = 16000000        'Quarzfrequenz&lt;br /&gt;
 $baud = 9600&lt;br /&gt;
&lt;br /&gt;
 Dim Rckanal As Word&lt;br /&gt;
&lt;br /&gt;
 Do&lt;br /&gt;
   Pulsein Rckanal , Pind , 2 , 1    'Messung Zeit zwischen 1 und 0 Pegel&lt;br /&gt;
   Print &amp;quot;RC Signal: &amp;quot; ; Rckanal ; &amp;quot;0 uS&amp;quot;&lt;br /&gt;
   Wait 2&lt;br /&gt;
 Loop&lt;br /&gt;
 End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GCC (Signallänge messen)==&lt;br /&gt;
Das GCC-Beispiel, das die gleiche Aufgabe erfüllt:&lt;br /&gt;
&lt;br /&gt;
Das ist nun nicht so einfach, da &amp;quot;PulseIn&amp;quot; eine komplexe Library-Funktion von BasCom ist, die für andere Sprachen normalerweise nicht verfügbar ist. Da BasCom für die Zählung keinen Timer verwendet, sondern die Maschinenzyklen als Maßstab nimmt, ist es in vergleichbarer Form eigentlich nur mit [[Inline-Assembler in avr-gcc|Inline-Assembler]] in Abhängigkeit von der Quarz-Frequenz zu lösen. Trotzdem soll zu Demonstration die Sache mit möglichst C-Style Mitteln durchgezogen werden.&lt;br /&gt;
&lt;br /&gt;
Im folgenden Beispiel ist aber nur die &amp;quot;PulseIn&amp;quot; Funktion ausgeführt, das &amp;quot;print&amp;quot; des Ergebnisses hatten wir ja schon.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt; &lt;br /&gt;
#include &amp;lt;avr/delay.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
#define BAUD_RATE    9600 &lt;br /&gt;
&lt;br /&gt;
#define PULS_C_LEV_LOW	0          // Die &amp;quot;LOW&amp;quot; Zeit des Pins soll gemessen werden &lt;br /&gt;
#define PULS_C_LEV_HIGH	0xFF       // Die &amp;quot;HIGH&amp;quot; Zeit des Pins soll gemessen werden &lt;br /&gt;
&lt;br /&gt;
#define NOP __asm__ __volatile (&amp;quot;nop&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
// !!! Dieses Konstrukt ist nur für PORTA bis PORTD anwendbar.&lt;br /&gt;
// !!! Für PORTE bis PORTG wird es so nicht funktionieren, und man muss sich etwas&lt;br /&gt;
// !!! anderes überlegen, weil die Portregister für diese Ports anders angeordnet sind.&lt;br /&gt;
typedef struct {&lt;br /&gt;
    volatile unsigned char bPin;              // PINx Register&lt;br /&gt;
    volatile unsigned char bDdr;              // DDRx Register&lt;br /&gt;
    volatile unsigned char bPort;             // PORTx Register&lt;br /&gt;
} IO_REG;&lt;br /&gt;
&lt;br /&gt;
unsigned short PulseIn (IO_REG* pPort, unsigned char PinNr, unsigned char Level);&lt;br /&gt;
&lt;br /&gt;
int main (void) &lt;br /&gt;
{ &lt;br /&gt;
unsigned short RcKanal;&lt;br /&gt;
&lt;br /&gt;
	RcKanal = PulseIn ((IO_REG*) &amp;amp;PIND, 2, PULS_C_LEV_HIGH);&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Hier ist ersteinmal die Verwendung der Funktion dargestellt. Es wird &lt;br /&gt;
*das Port mit Adresse angegeben     (PIND)&lt;br /&gt;
*die Pin-Nummer als Zahl 0-7        (2)&lt;br /&gt;
*Der Level, der gezählt werden soll (PULS_C_LEV_HIGH)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// -------------------------------------------------------------------------------------&lt;br /&gt;
// &lt;br /&gt;
// -------------------------------------------------------------------------------------&lt;br /&gt;
unsigned short PulseIn (IO_REG* pPort, unsigned char PinNr, unsigned char Level)&lt;br /&gt;
{&lt;br /&gt;
unsigned short wCounter = 0;			// Time Zähler&lt;br /&gt;
unsigned char mMask;	 	                // Die PIN-Maske  &lt;br /&gt;
&lt;br /&gt;
	mMask = 1 &amp;lt;&amp;lt; PinNr;		        // BitNr --&amp;gt; Bit-maske&lt;br /&gt;
	pPort-&amp;gt;bDdr &amp;amp;= ~mMask;  	        // define PINx as Input&lt;br /&gt;
	Level &amp;amp;= mMask;			        // Level (0 oder mMask)&lt;br /&gt;
&lt;br /&gt;
	while (Level == (pPort-&amp;gt;bPin &amp;amp; mMask))	// Warten auf PIN != Level&lt;br /&gt;
	{&lt;br /&gt;
		if (0 == ++wCounter) return (0);	// overflow --&amp;gt; return 0&lt;br /&gt;
	}				&lt;br /&gt;
&lt;br /&gt;
	wCounter = 0;				// reset&lt;br /&gt;
	while (Level != (pPort-&amp;gt;bPin &amp;amp; mMask))	// warten auf PIN == Level (Startflanke)&lt;br /&gt;
	{&lt;br /&gt;
		if (0 == ++wCounter) return (0);	// Überlauf --&amp;gt; return 0&lt;br /&gt;
	}				&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Soweit alles klar.&lt;br /&gt;
Bei dem folgenden Code muß man je nach verwendetem Optimizer die Verzögerung anpassen. Allerdings muß die '''gesamte''' Schleife berücksichtigt werden. In 10 µs vergehen &amp;lt;tt&amp;gt;F_CPU/100000&amp;lt;/tt&amp;gt; Zyklen, &lt;br /&gt;
bei einen Takt von 8 MHz geschehen in 10 µs also 80 Zyklen.&lt;br /&gt;
&lt;br /&gt;
Das Schleifen-Grundmuster wurde erstmal übersetzt (der Optimizer war auf &amp;quot;Standard&amp;quot; gestellt), und die *.LSS-File wurde kontrolliert. Man konnte die Zyklen nachzählen, die gesamte Schleife würde 16 Taktzyklen brauchen. Um pro Scheifendurchlauf auf 10 µs zu kommen, bauen wir noch eine innere Warteschleife mit &amp;lt;tt&amp;gt;_delay_loop_1&amp;lt;/tt&amp;gt; ein. Das Ausführen von &amp;lt;tt&amp;gt;_delay_loop_1(i)&amp;lt;/tt&amp;gt; dauert &amp;lt;tt&amp;gt;3&amp;amp;middot;i-1&amp;lt;/tt&amp;gt; Zyklen. Zusätzlich sehen wir zum Verzögen noch &amp;lt;tt&amp;gt;n&amp;lt;/tt&amp;gt; NOP-Operationen vor, die jeweils einen Takt dauern. Die 80 Zyklen setzen sich also folgendermassen zusammen:&lt;br /&gt;
 80 = 16 + 3*i-1 + n&lt;br /&gt;
was gleichbedeutend ist mit&lt;br /&gt;
 i = (80 - 16 + 1 -n) / 3 = (65-n)/3&lt;br /&gt;
Damit das beim Teilen durch 3 aufgeht und kein Rest bleibt, wählen wir &amp;lt;tt&amp;gt;n = 2 = 65 mod 3&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;i&amp;lt;/tt&amp;gt; ergibt sich zu &amp;lt;tt&amp;gt;i = 63/3 = 21&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	// Zeitmessung starten -------------------------------------------------------&lt;br /&gt;
	wCounter = 0;				// reset&lt;br /&gt;
	while (Level == (pPort-&amp;gt;bPin &amp;amp; mMask))  // zählen bis PIN != Level&lt;br /&gt;
	{&lt;br /&gt;
		_delay_loop_1 (21);&lt;br /&gt;
		NOP;&lt;br /&gt;
		NOP;&lt;br /&gt;
&lt;br /&gt;
		if (0 == ++wCounter) return (0);	// Überlauf (too long) --&amp;gt; return 0&lt;br /&gt;
	}&lt;br /&gt;
	return (wCounter);       // Ergebnis (0...65535) &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Diese Rechnung läßt sich wahrscheinlich nicht wirklich gut zu einer einfachen Formel für jede CPU-Frequenz verallgemeinern. Aber es sollte gezeigt werden, wie man zum Ziel kommen könnte, ohne gleich ein Assembler-Programm zu schreiben. Die Cycles der Befehle findet man übrigens im Datasheet des Controllers.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
==GCC GNU-Assembler (Signallänge messen)==&lt;br /&gt;
Das gleiche Beispiel, diesmal aber konsequenterweise gleich als [[GNU_Assembler|GNU-Assembler-Modul]].&lt;br /&gt;
Beim Hauptprogramm ändert sich nur die Funktionsdefinition auf &amp;quot;extern&amp;quot;&lt;br /&gt;
 extern unsigned short PulseIn(IO_REG* pPort, unsigned char PinNr, &lt;br /&gt;
                                                      unsigned char Level);&lt;br /&gt;
&lt;br /&gt;
In die Makefile wird diese Assembler-Source dazugeschrieben. Der Filetyp MUSS entweder &amp;quot;&amp;lt;tt&amp;gt;.S&amp;lt;/tt&amp;gt;&amp;quot; heissen,&lt;br /&gt;
oder per Kommandozeilen-Option &amp;quot;&amp;lt;tt&amp;gt;-x assembler-with-cpp&amp;lt;/tt&amp;gt;&amp;quot; muss gesagt werden, um welchen Dateityp es sich handelt. Und diese Source sieht folgendermassen aus: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define wDiv1  (F_CPU / 100000)&lt;br /&gt;
#define wIdle  (wDiv1 - 9) / 3			// Idle cycles  für 10µS 8MHZ&lt;br /&gt;
#define wNop   (wDiv1 - 9 - wIdle * 3)		// addit. Nops für 10µS 8MHZ&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das ist wieder die Rechnerei mit den Cpu-Cycles. Der Vorteil in Assembler ist es, daß wir nun selbst die Anzahl bestimmen können. &lt;br /&gt;
Weiter unten wird man sehen, daß die Zählschleife 9 Cycles benötigt, plus n * 3 Cycles für die zusätzliche Idle-Schleife. Das ergibt&lt;br /&gt;
 wIdle = (wDiv1 - 9) / 3		&lt;br /&gt;
das ergibt einen Divisionsrest von 0, 1 oder zwei&lt;br /&gt;
 wNop =  (wDiv1 - 9 - wIdle * 3)	&lt;br /&gt;
die werden unten dann mit &amp;quot;NOP&amp;quot; aufgefüllt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Für die Struktur IO_REG müssen wir nun ein Äquivalent schreiben&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#define bPin 	 	0&lt;br /&gt;
#define bDdr 	 	1&lt;br /&gt;
#define bPort 		2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 .global PulseIn&lt;br /&gt;
 .func	PulseIn&lt;br /&gt;
// r24:r25 R22:r23 r20:r21&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;.global&amp;lt;/tt&amp;gt; macht die Funktions-Adresse allen anderen Projekt-Module bekannt (die schreiben dafür &amp;quot;extern&amp;quot; in den function-header&lt;br /&gt;
*&amp;lt;tt&amp;gt;.func&amp;lt;/tt&amp;gt;  daß es eben eine Funktion mit diesem Namen ist&lt;br /&gt;
* r24:r25 R22:r23 r20:r21  Die Übergabe der Argumente erfolgt immer mit dem Registerpaar r24:25, und dann absteigend für die weiteren Argumente &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PulseIn:&lt;br /&gt;
	movw	r30, r24	; Pointer-Reg '''Z''' für IO_REG*   &lt;br /&gt;
&lt;br /&gt;
	in	r24, _SFR_IO_ADDR (SREG)	; SREG sichern&lt;br /&gt;
	push 	r24&lt;br /&gt;
        cli&lt;br /&gt;
&lt;br /&gt;
	clr	r18		; counter lo&lt;br /&gt;
	clr	r19		; counter hi&lt;br /&gt;
; Umwandeln der Pin-Nummer in eine Bit-Maske----------------------&lt;br /&gt;
	ldi	r21, 0x01	; &lt;br /&gt;
	tst	r22		; Pin-# ?&lt;br /&gt;
	breq	ShiftX		;&lt;br /&gt;
Shift:  &lt;br /&gt;
	add	r21, r21	; shift left Mask&lt;br /&gt;
	dec	r22		; count&lt;br /&gt;
	brne	Shift           ; repeat&lt;br /&gt;
ShiftX:    &lt;br /&gt;
; setzen Pin auf Input &lt;br /&gt;
	mov	r24, r21	; &lt;br /&gt;
	com	r24		; 1-compl Maske&lt;br /&gt;
	ldd	r25, Z+bDdr 	; Register  DDRx&lt;br /&gt;
	and	r25, r24	; auf Input setzen&lt;br /&gt;
	std	Z+bDdr, r25	;&lt;br /&gt;
&lt;br /&gt;
	and	r20, r21	; Level Argument (#3) &amp;amp; Maske&lt;br /&gt;
; warten auf Pin-Level &amp;lt;&amp;gt; Argument  &lt;br /&gt;
Wait_1:  			; wait for Pin state change&lt;br /&gt;
	ld	r24, Z		; bPin   (IO_REG* + 0)&lt;br /&gt;
	and	r24, r21	; mask pin&lt;br /&gt;
	cp	r24, r20	; argument ?&lt;br /&gt;
	brne	Wait_1X		; unequal, exit wait-1&lt;br /&gt;
	subi	r18, 0xFF	; Counter++, aber umgesetzt auf addieren low(-1)&lt;br /&gt;
	sbci	r19, 0xFF	;                                        high(-1)&lt;br /&gt;
	brne	Wait_1		; repeat&lt;br /&gt;
	rjmp	FuncExit	; overflow counter --&amp;gt; function Exit &lt;br /&gt;
Wait_1X:  &lt;br /&gt;
	clr		r18				; counter reset lo&lt;br /&gt;
	clr		r19				; counter reset hi	&lt;br /&gt;
&lt;br /&gt;
; warten auf Pin-Level = Argument  (startbedingung)&lt;br /&gt;
Wait_2: 			; wait for count-start edge&lt;br /&gt;
	ld	r24, Z		; bPin   (IO_REG* + 0)		&lt;br /&gt;
	and	r24, r21		&lt;br /&gt;
	cp	r24, r20		&lt;br /&gt;
	breq	Wait_2X		; gotcha ! &lt;br /&gt;
	subi	r18, 0xFF	; s.o.	&lt;br /&gt;
	sbci	r19, 0xFF	; s.o.	&lt;br /&gt;
	brne	Wait_2		; repeat	&lt;br /&gt;
	rjmp	FuncExit	; overflow counter --&amp;gt; function Exit &lt;br /&gt;
Wait_2X:  &lt;br /&gt;
	clr		r18				; counter reset lo&lt;br /&gt;
	clr		r19				; counter reset hi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt kommt wieder die Zählschleife, wo alle Cycles gezählt werden müssen &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
;---------------------------------------------------------&lt;br /&gt;
; start counting loop 	 &lt;br /&gt;
;---------------------------------------------------------&lt;br /&gt;
Wait_C:  						 &lt;br /&gt;
	ld	r24, Z		; 2cyc&lt;br /&gt;
	and	r24, r21	; 1cyc&lt;br /&gt;
	cp	r24, r20	; 1cyc&lt;br /&gt;
	brne	FuncExit	; 1/2cyc 	Pin &amp;lt;&amp;gt; Argument&lt;br /&gt;
		&lt;br /&gt;
	ldi	r24, wIdle	; 1cyc&lt;br /&gt;
Idle:&lt;br /&gt;
	dec	r24		; 1cyc&lt;br /&gt;
	brne	Idle		; 2  / 1cyc &lt;br /&gt;
#if (wNop &amp;gt;= 1)         	; wenn divisionrest (s.o.)&lt;br /&gt;
	nop			; 1cyc	 ein nop einfügen 	&lt;br /&gt;
#endif		&lt;br /&gt;
#if (wNop &amp;gt;= 2) &lt;br /&gt;
	nop			; 1cyc	 ein zweites nop einfügen&lt;br /&gt;
#endif		&lt;br /&gt;
	subi	r18, 0xFF	; 1cyc&lt;br /&gt;
	sbci	r19, 0xFF	; 1cyc&lt;br /&gt;
	brne	Wait_C		; 2cyc	 &lt;br /&gt;
FuncExit:&lt;br /&gt;
	pop 	r24&lt;br /&gt;
	out	_SFR_IO_ADDR (SREG), r24 	; SREG&lt;br /&gt;
&lt;br /&gt;
	movw	r24, r18	; return (counter)&lt;br /&gt;
	ret&lt;br /&gt;
.endfunc&lt;br /&gt;
.end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Ein Funktionsergebnis wird immer mit r24:r25 zurückgegeben&lt;br /&gt;
&lt;br /&gt;
*Anmerkung: das Umsetzen der Zähler-Inkrementierung (counter++) auf &lt;br /&gt;
 subi	r18, 0xFF&lt;br /&gt;
 sbci	r19, 0xFF&lt;br /&gt;
ist notwendig, weil es für 16-Bit keine inkrement-Anweisung gibt. Nur für die Registerpaare r24:r25 - r30:r31  gibt es &amp;quot;ADIW&amp;quot;. Daß dieser Befehl aber dann ebenso 2 Cycles braucht, hilft es im Grunde gar nichts.&lt;br /&gt;
&lt;br /&gt;
*Noch etwas: Da ja bei der Zählung keine Interrupts erwünscht sind, werden während der Funktion die Interrupt generell disabled. Vielleicht sind sie das aber garnicht. &lt;br /&gt;
Gängig ist der folgende Weg:&lt;br /&gt;
*Wir sichern zu Beginn das Status-register (wo dieser enable-Flag ja drinnen steht)&lt;br /&gt;
*dann machen wir so oder so CLI (disable Interrupts)&lt;br /&gt;
*Am Ende sagen wir aber nicht &amp;quot;SEI&amp;quot;, sondern stellen einfach nur das SREG wieder her, zusammen mit dem Global Interrupt enable-Flag. Dadurch wird der Zustand einfach wieder hergestellt.&amp;lt;br/&amp;gt;&lt;br /&gt;
Zu Beginn:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        in	r24, 0x3F	; SREG&lt;br /&gt;
	push 	r24             ; sichern auf den Stack&lt;br /&gt;
        cli                     ; disablen Interrupts&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Am Ende:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	pop 	r24             ; holen vom Stack&lt;br /&gt;
	out	0x3F, r24 	; SREG wiederherstellen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
=Externe Interrupts=&lt;br /&gt;
Dieses kleine Beispiel demonstriert, wie man in Bascom und GCC Interrupt-Routinen anlegt. Also Programmzeilen, die nur dann ausgeführt werden, wenn ein Low/High Pegel an einem bestimmten PIN eines Controllers wechselt. Bei einem solchen Wechsel wird das Hauptprogramm unterbrochen und die Interrupt-Routine aufgerufen. Anschließend wird das Hauptprogramm wieder weiter ausgeführt als sei nix gewesen. In diesem Beispiel macht das Hauptprogramm garnichts, es ist nur eine Endlosschleife. Die Interruptroutine schalten bei jedem Aufruf den Zustand einer LED um.&lt;br /&gt;
&lt;br /&gt;
==BasCom (Externe Interrupts)==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $regfile = &amp;quot;m32def.dat&amp;quot;  'z.B. rn-control &lt;br /&gt;
  $framesize = 32 &lt;br /&gt;
  $swstack = 32 &lt;br /&gt;
  $hwstack = 32 &lt;br /&gt;
  $crystal = 16000000                 'Quarzfrequenz &lt;br /&gt;
  $baud = 9600 &lt;br /&gt;
&lt;br /&gt;
  Config Pinc.2 = Output  'An dem PIN sollte LED sein &lt;br /&gt;
  Led3 Alias Portc.2 'Hier geben wir der Definition einen schöneren Namen &lt;br /&gt;
  Config Int0 = RISING  'Interrupt bei steigender Flanke&lt;br /&gt;
&lt;br /&gt;
  On Int0 Irq0  'Festlegen wo bei externem Interrupt hin gesprungen wird&lt;br /&gt;
  Enable Int0   'Diesen Interrupt aktivieren&lt;br /&gt;
  Enable Interrupts  'Alle aktivierten Interrupts einschalten&lt;br /&gt;
  Do  'Endlosschleife&lt;br /&gt;
  Loop &lt;br /&gt;
  End &lt;br /&gt;
&lt;br /&gt;
  'Interrupt Routine wird immer ausgelöst wenn der Pegel von 0 auf 1 am&lt;br /&gt;
  'INT0 (Pin 18 PD2) Eingang wechselt &lt;br /&gt;
  Irq0: &lt;br /&gt;
    Toggle Led3 &lt;br /&gt;
  Return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GCC (Externe Interrupts)==&lt;br /&gt;
Das GCC-Beispiel, das die gleiche Aufgabe erfüllt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define LED3 (1 &amp;lt;&amp;lt; PC2)                         &lt;br /&gt;
&lt;br /&gt;
/* Interrupt Routine wird immer ausgelöst wenn der Pegel von 0 auf 1 am&lt;br /&gt;
   INT0 (Pin 18 PD2) Eingang wechselt */&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
  PORTC ^= LED3;				// Pin wechseln&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//-----------------------------------------------------&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  /* Ports und Interrupts initialisieren */&lt;br /&gt;
  DDRC |= LED3;					// PC2 als Ausgang&lt;br /&gt;
  DDRD &amp;amp;= ~(1 &amp;lt;&amp;lt; DDD2);				// PD2 als Eingang (ext. Interrupt 0)&lt;br /&gt;
  MCUCR |= ((1 &amp;lt;&amp;lt; ISC01) | (1 &amp;lt;&amp;lt;ISC00));	// steigende Flanke an INT0 erzeugt einen Interrupt  &lt;br /&gt;
  GICR  |= (1 &amp;lt;&amp;lt; INT0);				// Diesen Interrupt aktivieren &lt;br /&gt;
&lt;br /&gt;
  sei();        				// Alle aktivierten Interrupts einschalten&lt;br /&gt;
&lt;br /&gt;
  while(1);					// Endlosschleife &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==AVRStudio Assembler (Externe Interrupts)==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.NOLIST                    ; List-Output unterdrücken&lt;br /&gt;
 .INCLUDE &amp;lt;m32def.inc&amp;gt;       ; das gibt es für jeden Controllertyp&lt;br /&gt;
 .LIST                      ; List-Output wieder aufdrehen&lt;br /&gt;
 .CSEG                      ; was nun folgt, gehört in den FLASH-Speicher&lt;br /&gt;
&lt;br /&gt;
 #define LED3 		PC2                         &lt;br /&gt;
&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;     Start Adresse 0000&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 RESET:&lt;br /&gt;
 	jmp INIT           ; springen nach &amp;quot;INIT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 .ORG  INT0ADDR			; Vector für INT0&lt;br /&gt;
	jmp		IsrInt0&lt;br /&gt;
&lt;br /&gt;
 .ORG INT_VECTORS_SIZE    ; dadurch haben wir für die Vektoren Platz gelassen&lt;br /&gt;
 INIT:  &lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;     INITIALIZE&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 	ldi 	r24,high(RAMEND)     ;Stack Pointer setzen &lt;br /&gt;
 	out 	SPH,r24              ; &amp;quot;RAMEND&amp;quot; ist in m32def.inc (s.o.) festgelegt&lt;br /&gt;
 	ldi 	r24,low(RAMEND)      ; &lt;br /&gt;
 	out 	SPL,r24              ;&lt;br /&gt;
&lt;br /&gt;
	sbi	DDRC,  LED3			; PC2 als Ausgang&lt;br /&gt;
	cbi 	DDRD,  PD2			; PD2 als Eingang (ext. Interrupt 0)&lt;br /&gt;
	in	r24, MCUCR			; steigende Flanke an INT0 erzeugt einen Interrupt  &lt;br /&gt;
	ori	r24, (1 &amp;lt;&amp;lt; ISC01) | (1 &amp;lt;&amp;lt; ISC00) &lt;br /&gt;
	in	r24, GICR&lt;br /&gt;
  	ori	r24,  INT0			; Diesen Interrupt aktivieren &lt;br /&gt;
	out	GICR, r24&lt;br /&gt;
&lt;br /&gt;
	sei        				// Alle aktivierten Interrupts einschalten&lt;br /&gt;
&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;   HAUPTSCHLEIFE&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 Hauptschleife: &lt;br /&gt;
    rjmp 	Hauptschleife         ; immer wiederholen &lt;br /&gt;
&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
 ;   INTERRUPT&lt;br /&gt;
 ;------------------------------------------------------&lt;br /&gt;
IsrInt0:&lt;br /&gt;
	push	r22                   ; In diesem Beispiel eigentlich nicht notwendig&lt;br /&gt;
	push	r24                   ; In diesem Beispiel eigentlich nicht notwendig&lt;br /&gt;
	in	r24, SREG             ; In diesem Beispiel eigentlich nicht notwendig&lt;br /&gt;
	push	r24                   ; In diesem Beispiel eigentlich nicht notwendig&lt;br /&gt;
&lt;br /&gt;
	ldi	r22, (1 &amp;lt;&amp;lt; LED3)&lt;br /&gt;
	in	r24, PORTC&lt;br /&gt;
	eor	r24, r22&lt;br /&gt;
   	out	PORTC, r24&lt;br /&gt;
&lt;br /&gt;
	pop	r24                   ; In diesem Beispiel eigentlich nicht notwendig&lt;br /&gt;
	out	SREG, r24             ; In diesem Beispiel eigentlich nicht notwendig&lt;br /&gt;
	pop	r24                   ; In diesem Beispiel eigentlich nicht notwendig&lt;br /&gt;
	pop	r22                   ; In diesem Beispiel eigentlich nicht notwendig&lt;br /&gt;
	reti&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
;Bemerkung: die PUSH / POP Befehle sind in diesem Sonderfall, daß in der Hauptschleife nichts geschieht, überflüssig. Aber man sollte sich angewöhnen, in eine ISR-Routine den Status und die verwendeten Register zu sichern.&lt;br /&gt;
&lt;br /&gt;
=Assembler-Schnippsel=&lt;br /&gt;
&lt;br /&gt;
In einer Hochsprache will man sich nicht mit Assembler rumbalgen. Schliesslich hat man sich für einen Compiler entschieden, damit dieser die Register-Verwaltung übernimmt, die Werte von Funktionsaufrufen verwaltet, den Code beim Betreten und Beenden einer Funktion oder Interrupt Service Routine erzeugt, etc.&lt;br /&gt;
&lt;br /&gt;
Jedoch kann nicht alles, was auf der untersten Ebene möglich ist, auf Hochsprachen-Ebene dargestellt werden. Eine Bibliotheksfunktion muss eine bestimmten Ansatz wählen, und eine Sprache wie C für jeden der tausenden von Controllertypen zu erweitern und &amp;quot;aufzubohren&amp;quot;, würde lediglich zu einer babylonischen Verwirrung führen.&lt;br /&gt;
&lt;br /&gt;
In speziellen Fällen wird man dann Lösungen auf der untersten Ebene formulieren wollen/müssen und ist froh, die Möglichkeit der Hochsprache nutzen zu können, um Assembler-Schnippsel in den Code einzufügen.&lt;br /&gt;
&lt;br /&gt;
Dabei soll hier nicht interessieren, wie der entsprechende Schnippsel in der Hochsprache nachgebildet werden könnte, sondern wie ein vorgegebener Schnippsel &amp;amp;ndash; teilweise auch als &amp;quot;Inline Assembler&amp;quot; bezeichnet &amp;amp;ndash; in den Code eingefügt wird, und wie das Interface zwischen Hochsprache und Assembler aussieht.&lt;br /&gt;
&lt;br /&gt;
==GNU Assembler (Assembler einfügen)==&lt;br /&gt;
Der Schnippsel, der eingefügt werden soll, berechnet die Anzahl der gesetzten Bits in einem Byte. Der Eingabe-Wert, dessen Bits gezählt werden sollen, steht in Register r26 und wird zerstört, das Ergebnis steht in r25, der Inhalt von r24 wird zudem zersört:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   clc                  ; das Carry-Flag auf 0 setzen&lt;br /&gt;
   ldi r24, 0           ; Konstante 0 in Register 24 laden &lt;br /&gt;
   ldi r25, 0           ; Das Ergebnis auf 0 initialisieren&lt;br /&gt;
bitcount_loop:&lt;br /&gt;
   adc r25, r24         ; 0 und Carry auf r25 addieren&lt;br /&gt;
   lsr r26              ; die Eingabe 1 nach rechts ins Carry schieben&lt;br /&gt;
   brne bitcount_loop   ; Loop, bis in der Eingabe keine Bits mehr gesetzt sind&lt;br /&gt;
   adc r25, r24         ; das letzte Bit nicht vergessen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GCC (Assembler einfügen)==&lt;br /&gt;
Der einzufügende Assembler-Schnippsel ist für [[avr-gcc|gcc]] lediglich ein String, der in die Ausgabe eingefügt wird. gcc hat keine Vorstellung davon, was innerhalb des [[Inline-Assembler in avr-gcc|Inline-Assembler]]-Schnippsels abgeht. &lt;br /&gt;
Deshalb muss man selbst eine Verbindung zwischen Eingaberegister und -wert bzw. Ausgeberegister und -wert herstellen, und sagen, welche Register sich auf unkontrollerte Art und Weise ändern.&lt;br /&gt;
&lt;br /&gt;
Bereits dieses einfache Beispiel ist tückisch. Hier erst mal der Code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
static inline unsigned char bitcount (unsigned char eingabe)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t count;&lt;br /&gt;
   __asm__ __volatile (&lt;br /&gt;
      &amp;quot;   clc          \n&amp;quot;&lt;br /&gt;
      &amp;quot;   ldi r25, 0   \n&amp;quot;&lt;br /&gt;
      &amp;quot;   ldi %0, 0    \n&amp;quot;&lt;br /&gt;
      &amp;quot;0:              \n&amp;quot;&lt;br /&gt;
      &amp;quot;   adc %0, r25  \n&amp;quot;&lt;br /&gt;
      &amp;quot;   lsr %1       \n&amp;quot;&lt;br /&gt;
      &amp;quot;   brne 0b      \n&amp;quot;&lt;br /&gt;
      &amp;quot;   adc %0, r25&amp;quot;&lt;br /&gt;
         : &amp;quot;=&amp;amp;r&amp;quot; (count), &amp;quot;=r&amp;quot; (eingabe)&lt;br /&gt;
         : &amp;quot;1&amp;quot; (eingabe)&lt;br /&gt;
         : &amp;quot;r25&amp;quot;&lt;br /&gt;
   );&lt;br /&gt;
&lt;br /&gt;
   return count;&lt;br /&gt;
}	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die einzelnen Argumente des Inline Assembler-Kommandos werden durch einen Doppelpunkt &amp;lt;tt&amp;gt;:&amp;lt;/tt&amp;gt; abgetrennt, und nicht wie von C gewohnt mit einem Komma.&lt;br /&gt;
; Argument 1&amp;lt;nowiki&amp;gt;:&amp;lt;/nowiki&amp;gt; Assembler-Code: Der Text, der in die Ausgabe eingefügt werden soll (das ''template'' (die Schablone)). Dieser String wird genau so in den Ausgabestrom eingefügt, nachdem die %-Platzhalter ersetzt wurden. Die Registerzuordnungen kann man teilweise selbst wählen, indem man wie hier r25 verwendet wie im Assembler auch. Für die Variablen gibt es die Platzhalter &amp;lt;tt&amp;gt;%0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;%1&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;%2&amp;lt;/tt&amp;gt;, denn es folgen drei Operanden (&amp;lt;tt&amp;gt;count&amp;lt;/tt&amp;gt; ist Ausgabe, &amp;lt;tt&amp;gt;eingabe&amp;lt;/tt&amp;gt; ist auch Ausgabe (es wird verändert!), &amp;lt;tt&amp;gt;eingabe&amp;lt;/tt&amp;gt; ist Eingabe). Für die Variablen trifft gcc die Entscheidung, welche Register verwendet werden.&lt;br /&gt;
:Weil der Schnippsel mehrfach vorkommen kann, ist es nicht möglich, der Sprungmarke in der Schleife einen festen Namen zu geben, denn das würde bei Mehrfachverwendung einen Fehler beim Assemblieren geben. Statt dessen wird in der Schleife zum nächsten auffindbaren &amp;quot;Wegwerf-Label&amp;quot; &amp;lt;tt&amp;gt;0:&amp;lt;/tt&amp;gt; gesprungen, wobei der Sprung nach rückwärts erfolgt (angegeben durch das &amp;lt;tt&amp;gt;0b&amp;lt;/tt&amp;gt; für &amp;quot;backward&amp;quot; im Sprungbefehl. Ein vorwärts wäre übrigens ein &amp;lt;tt&amp;gt;0f&amp;lt;/tt&amp;gt; für &amp;quot;forward&amp;quot;. Verwendbar sind die Ziffern &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; bis &amp;lt;tt&amp;gt;9&amp;lt;/tt&amp;gt;).&lt;br /&gt;
; Argument 2&amp;lt;nowiki&amp;gt;:&amp;lt;/nowiki&amp;gt; Die Ausgabe-Operanden: Das '''=''' kennzeichnet einen Operanden als Ausgabe-Operand, und das '''r''' legt die Registerklasse fest. Die Registerklasse '''r''' umfasst alle 32 Register, denn die verwendeten Befehle sind auf alle 32 Register anwendbar. (Das gilt nicht für alle AVR-Instruktionen, so ist &amp;lt;tt&amp;gt;ldd&amp;lt;/tt&amp;gt; nur mit Y- und Z-Register anwendbar.) Hinter der Registerklasse steht in Klammern der C-Ausdruck, der dem Register entspricht. Mehrere Operanden werden durch ein '''&amp;lt;tt&amp;gt;,&amp;lt;/tt&amp;gt;''' voneinander getrennt.&lt;br /&gt;
; Argument 3&amp;lt;nowiki&amp;gt;:&amp;lt;/nowiki&amp;gt; Die Eingabe-Operanden: So aufgebaut wie die Ausgabe-Operanden, allerdings ohne '''='''. Die &amp;lt;tt&amp;gt;&amp;quot;1&amp;quot;&amp;lt;/tt&amp;gt; bedeutet, daß dieser Operand (Nummer 2) mit Operand 1 übereinstimmt, und sich im selben Register befindet. Daher gibt es im Assembler-String auch kein Platzhalter &amp;lt;tt&amp;gt;%2&amp;lt;/tt&amp;gt;. Ausser C-Variablen sind auch komplexe Ausdrücke und Funktionsaufrufe erlaubt (je nach Constraint).&lt;br /&gt;
; Argument 4&amp;lt;nowiki&amp;gt;:&amp;lt;/nowiki&amp;gt; Die überschriebenen Register: gibt an, welche Register nach der Assembler-Sequenz keinen definierten Zustand haben (bzw. deren Zustand wertlos ist). Anstatt r25 selbst auszuwählen, hätte man auch eine C-Variable anlegen und übergeben können. Mit einer C-Variablen hätte gcc die Freiheit der Entscheidung, welches Register verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Der ganze Code steht innerhalb einer Inline-Funktion, wird also wie angegeben an die jeweilige Aufrufstelle von &amp;lt;tt&amp;gt;bitcount&amp;lt;/tt&amp;gt; eingefügt. Dies begrenzt den Seiteneffekt, daß &amp;lt;tt&amp;gt;eingabe&amp;lt;/tt&amp;gt; zerstört wird, auf die (Inline-)Funktion.&lt;br /&gt;
&lt;br /&gt;
=State Machine=&lt;br /&gt;
&lt;br /&gt;
Eine '''State Machine''' ist ein Programmier-Werkzeug.&lt;br /&gt;
&lt;br /&gt;
Oft besteht ein Programmcode aus langwierigen if-then-else-Abfragen bzw switch-case oder select Anweisungen. Ein Beispiel für solchen Code ist die Menüsteuerung in einem Händi. Je nachdem, in welchem Menü-Punkt man sich befindet, sind unterschiedliche Aktionen möglich oder Tasten unterschiedlich belegt, oder eine Taste hat keine Funktion. Ein weiteres Beispiel kann ein Roboter sein, der zwischen verschiedenen Zuständen oder Aufgaben hin- und herwechselt: Gegenstand suchen, Gegenstand gefunden, Lichtquelle suchen, Akku muss geladen werden, Bedien-Modus (evtl. auch über Menu-Struktur), oder was auch immer denkbar ist. Und selbst Hardware wie die Zustände des [[TWI]] bei ATmega sind als State-Machine beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für kleinere Aufgaben kommt man mit dem if-else-Ansatz schnell voran. Bei kompexeren Dingen schreibt man aber immer einen fast gleichen Abfrage-Code, der sich nur durch Kleinigkeiten unterscheidet. Zum einen wuchert der Code schnell aus und wird unübersichtlich, zum anderen wünscht man sich, schnell und zentral die Struktur-Informationen zu bündeln, und von dem immer gleichen Code zu befreien. &lt;br /&gt;
&lt;br /&gt;
Eine State-Machine löst dieses Problem.&lt;br /&gt;
&lt;br /&gt;
Damit spart man nicht zuletzt auch deutlich Platz! Denn die Übergangs- und Zustands-Tabellen einer State-Machine enthalten die reinen Nutz-Informationen, die sonst quer verstreut im Code als Werte von Abfragen enthalten sind. Dadurch befreit man das Programm von jeglichem Ballast.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich trennen wir dadurch die Implementierung von den Funktionen, die jeweils ausgelöst werden. Das schafft Klarheit und Struktur, ohne ineffizient zu sein.&lt;br /&gt;
&lt;br /&gt;
Die Aktionen, die auszuführen sind, können zugeordnet werden. Etwa &lt;br /&gt;
* bei Zustandsänderung von A nach B&lt;br /&gt;
* wenn man einen Zustand X betritt&lt;br /&gt;
* während Zustand Y immer wieder eine Aktion ausführen lassen, evtl. in einem bestimmten Zeitraster&lt;br /&gt;
&lt;br /&gt;
Weil das ganze sehr universell ist, soll als Code-Beispiel exemplarisch eine der bekanntesten und universellsten State-Machinen überhaupt gezeigt werden: Eine '''Turing-Maschine''' (TM). &lt;br /&gt;
&lt;br /&gt;
Auch sie hat unterschiedliche Zustände, sowie eine Eingabe (das unendlich lange &amp;quot;Band&amp;quot;). Von dort liest sie ein Zeichen, und anhand ihres Zustands und des gelesenen Zeichens führt sie eine einfache Aktion aus: &lt;br /&gt;
# Sie schreibt ein Zeichen an die aktuelle Bandposition (oder schreibt nichts)&lt;br /&gt;
# verschiebt den Schreib-/Lese-Zeiger des Bands um maximal eine Position nach links/rechts (oder bleibt auf der Position stehen)&lt;br /&gt;
# und nimmt einen neuen Zustand an (oder bleibt eben im alten Zustand)&lt;br /&gt;
&lt;br /&gt;
Das Beispiel implementiert eine kleine Turing-Maschine (TM), die eine Eingabe aus ''n'' Einsen '''1''' bekommt, die mit einem Ende-Zeichen (dargestellt als '''.''') abgeschlossen ist. Die TM verdoppelt diese Eingabe, indem sie nochmal ''n'' Einsen und ein Ende-Zeichen rechts dranhängt. Nimmt man die Einsen als Repräsentation der Zahl ''n'', kann das als Multiplikation mit 2 gedeutet werden. Im Beispiel bleibt aber noch ein Trennzeichen zwischen den Einser-Gruppen übrig.&lt;br /&gt;
&lt;br /&gt;
Eine Eingabe könnte auch irgend etwas anderes sein: Ein Tastendruck, empfangenes Kommando über eine Schnittstelle, Sensor- oder Zähler-Wert, etc.&lt;br /&gt;
&lt;br /&gt;
Unsere TM hat 7 Zustände: START (S), S1 bis S5, und END (E). Beobachten wir mal, wie sie arbeitet. Das Zeichen unter dem Schrei-/Lesekopf ist rot, und der Schnappschuss entstand, bevor die TM ihre Aktion gemacht hat. Falls der Bandzustand noch undefiniert ist, wird das als '''?''' angezeigt.&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| width=&amp;quot;500&amp;quot;&lt;br /&gt;
|+ '''Arbeitsschritte unserer Turing-Maschine&amp;lt;br/&amp;gt;mit der Eingabe &amp;quot;&amp;lt;tt&amp;gt;11.&amp;lt;/tt&amp;gt;&amp;quot;'''&lt;br /&gt;
|- &lt;br /&gt;
{| {{Blauetabelle}}&lt;br /&gt;
|&amp;lt;tt&amp;gt;&lt;br /&gt;
S:: &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;1&amp;lt;/font&amp;gt;1.???&amp;lt;br/&amp;gt;&lt;br /&gt;
S1: .&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;1&amp;lt;/font&amp;gt;.???&amp;lt;br/&amp;gt;&lt;br /&gt;
S1: .1&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;.&amp;lt;/font&amp;gt;???&amp;lt;br/&amp;gt;&lt;br /&gt;
S2: .1.&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;?&amp;lt;/font&amp;gt;??&amp;lt;br/&amp;gt;&lt;br /&gt;
S3: .1.1&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;?&amp;lt;/font&amp;gt;?&amp;lt;br/&amp;gt;&lt;br /&gt;
S4: .1.&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;1&amp;lt;/font&amp;gt;.?&amp;lt;br/&amp;gt;&lt;br /&gt;
S4: .1&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;.&amp;lt;/font&amp;gt;1.?&amp;lt;br/&amp;gt;&lt;br /&gt;
S5: .&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;1&amp;lt;/font&amp;gt;.1.?&amp;lt;br/&amp;gt;&lt;br /&gt;
S5: &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;.&amp;lt;/font&amp;gt;1.1.?&amp;lt;br/&amp;gt;&lt;br /&gt;
S:: 1&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;1&amp;lt;/font&amp;gt;.1.?&amp;lt;br/&amp;gt;&lt;br /&gt;
S1: 1.&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;.&amp;lt;/font&amp;gt;1.?&amp;lt;br/&amp;gt;&lt;br /&gt;
S2: 1..&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;1&amp;lt;/font&amp;gt;.?&amp;lt;br/&amp;gt;&lt;br /&gt;
S2: 1..1&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;.&amp;lt;/font&amp;gt;?&amp;lt;br/&amp;gt;&lt;br /&gt;
S3: 1..11&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;?&amp;lt;/font&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
S4: 1..1&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;1&amp;lt;/font&amp;gt;.&amp;lt;br/&amp;gt;&lt;br /&gt;
S4: 1..&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;1&amp;lt;/font&amp;gt;1.&amp;lt;br/&amp;gt;&lt;br /&gt;
S4: 1.&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;.&amp;lt;/font&amp;gt;11.&amp;lt;br/&amp;gt;&lt;br /&gt;
S5: 1&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;.&amp;lt;/font&amp;gt;.11.&amp;lt;br/&amp;gt;&lt;br /&gt;
S:: 11&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;.&amp;lt;/font&amp;gt;11.&amp;lt;br/&amp;gt;&lt;br /&gt;
E:: 11&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;.&amp;lt;/font&amp;gt;11.&amp;lt;br/&amp;gt;&lt;br /&gt;
Lauf ausgeführt.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe wurde übrigens mit dem C-Beispiel erstellt, das aus der '''gleichen Quelle''' für einen PC erzeugt und dort laufen gelassen wurde!&lt;br /&gt;
&lt;br /&gt;
==Suchen und Finden==&lt;br /&gt;
&lt;br /&gt;
In dem Beispiel wird mit Tabellen (Arrays) gearbeitet, in denen nach Informationen wie &amp;quot;Zustand&amp;quot; oder &amp;quot;Übergangsfunktion/Aktion&amp;quot; gesucht werden. Dafür gibt es verschiedene Möglichkeiten, die alle ihre Vor- und Nachteile haben. &lt;br /&gt;
&lt;br /&gt;
Bei einer Maschine mit ''N'' Zuständen und ''E'' verschiedenen Ereignissen, die einem Zustandswechsel verursachen, hat man schon ''N''&amp;amp;middot;''E'' Möglichkeiten für einen Zustandswechsel.&lt;br /&gt;
&lt;br /&gt;
;Zugriff über den Index (Position): Damit findet man sehr schnell die Information. Aus der Tabelle der Zustände wird der Zustand mit der Nummer ''n'' gelesen, indem man auf das ''n''-te Element zugreift. Die Zustands-Nummer muss nicht im Zustand selber gespeichert werden. Diese Art des Findens eignet sich für dicht besetzte Tabellen, in denen es kaum/keine Lücken gibt. (Falls es welche gibt, muss man das erkennen können). Im Beispiel wird auf die Zustands-Tabelle so zugegriffen, auch wenn sie dort recht leer ist. In der Praxis sieht das aber oft anders aus.&lt;br /&gt;
;Tabelle durchsuchen: Diese Herangehensweise eignet sich für Daten, die eine fast leere (&amp;quot;dünn besetzte&amp;quot;) Tabelle zur Folge hätten. Im Beispiel wird die Tabelle mit den Übergangs-Beschreibungen durchsucht. Dazu braucht man einen ''Schlüssel''. Im Beispiel wird dieser gebildet aus dem aktuellen Zustand der Maschine und dem vom Band gelesenen Zeichen. Für jeden existierenden Übergang gibt es dann einen Eintrag in der Tabelle, allerdings ohne Lücken dazwischen. &lt;br /&gt;
;Tabelle binär durchsuchen: Die beim Suchen verwendet Tabelle ist zwar kompakt weil sie keine Lücken hat, aber man muss sich im Zweifelsfalle jeden Eintrag anschauen, um einen Eintrag zu finden oder um zu Erkennen, daß es gar keinen Eintrag gibt. Das geht ab einer bestimmten Tabellengröße wesentlich schneller, wenn man eine binäre Suche verwenden kann (im Beispiel nicht verwendet). Dadurch reduziert sich die Zeit zum Durchsuchen einer Tabelle mit ''M'' Einträgen von der Größenordnung ''M'' zur Größenordnung log&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;(''M''). Es ist jedoch etwas Programmierung zu leisten, und für sehr kleine Tabellen ist die binäre Suche unterlegen. Bei einer Tabelle mit 100 Einträgen braucht man 100 Vergleiche ohne binäre Suche und ca 8 Vergleiche mit binärer Suche.&lt;br /&gt;
&lt;br /&gt;
==GCC (State Machine)==&lt;br /&gt;
&lt;br /&gt;
Der C-Code ist etwas länger, weil wir damit auch noch die Ausgabe erzeugen und den Code auf einem PC ausführen/testen können. Diese (relative) Plattform-Unabhängig ist typisch für C. Ausserdem machen wir einen kleinen Konsistenz-Test.&lt;br /&gt;
&lt;br /&gt;
Besonders interessant ist die Möglichkeit, Funktionen indirekt aufzurufen. Dadurch können wir auch Funktions-Adressen in die Datenstrukturen aufnehmen und später aufrufen!&lt;br /&gt;
In einer Objekt-orientierten Sprache würden diese Funktionen den ''Methoden'' eines Objektes entsprechen, während die &amp;quot;normalen&amp;quot; Daten das Analogon zu den ''Eigenschaften'' eines Objekts sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Blauetabelle}}&lt;br /&gt;
|+ '''Tabelle: Flash-Verbrauch von [[avr-gcc]] in Byte'''&lt;br /&gt;
|- {{Hintergrund1}}&lt;br /&gt;
!colspan=&amp;quot;3&amp;quot;| Funktion&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;tt&amp;gt;turing_lauf()&amp;lt;/tt&amp;gt;   || 136 || Band erzeugen und Eingabe hinkopieren&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;tt&amp;gt;turing()&amp;lt;/tt&amp;gt;        || 144 || TM laufen lassen&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;tt&amp;gt;job_S2_write1()&amp;lt;/tt&amp;gt; || 16  || ein Handler&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;tt&amp;gt;job_S3_write0()&amp;lt;/tt&amp;gt; || 14  || ein Handler&lt;br /&gt;
|- {{Hintergrund1}}&lt;br /&gt;
!colspan=&amp;quot;3&amp;quot;| Tabelle&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;states[]&amp;lt;/tt&amp;gt;        || 14  || die Zustände&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;transits[]&amp;lt;/tt&amp;gt;      || 54  || die Übergänge&lt;br /&gt;
|- {{Hintergrund1}}&lt;br /&gt;
!colspan=&amp;quot;3&amp;quot;| Lib-Funktion&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;tt&amp;gt;strcpy()&amp;lt;/tt&amp;gt;        || 14  || Zeichenkette kopieren&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Systemübergreifende Includes und Defines'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
// Systemübergreifende Includes und Defines&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Systemabhängige Includes und Defines'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
// Systemabhängige Includes und Defines&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
#ifdef __AVR__&lt;br /&gt;
&lt;br /&gt;
// Für AVR machen wir Flash-Zugriff, denn die Tabellen unten&lt;br /&gt;
// werden niemals verändert.&lt;br /&gt;
// (falls das nötig scheint, hat man mit Sicherheit einen Design-Fehler) &lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#else // __AVR__&lt;br /&gt;
&lt;br /&gt;
// Auf einem PC machen wir Ausgaben und bilden &lt;br /&gt;
// AVR-spezifisches Zeug auf Standard-C ab.&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#define PROGMEM&lt;br /&gt;
#define pgm_read_byte(addr)  (*(addr))&lt;br /&gt;
#define pgm_read_word(addr)  (*(addr))&lt;br /&gt;
#define pgm_read_dword(addr) (*(addr))&lt;br /&gt;
&lt;br /&gt;
#endif // __AVR__&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Verwendete Datenstrukturen definieren'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
// Verwendete enums und Strukturen&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
// Zustände unserer Turing-Maschine&lt;br /&gt;
enum&lt;br /&gt;
{&lt;br /&gt;
    TM_FATAL = 0, // wir haben einen Fehler in der Beschreibung!&lt;br /&gt;
    TM_START,&lt;br /&gt;
    TM_S1, TM_S2, TM_S3, TM_S4, TM_S5,&lt;br /&gt;
    TM_END&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Die Aktion:&lt;br /&gt;
// Wir schreiben 'wert' auf das Band&lt;br /&gt;
// (oder nichts, falls wert == -1),&lt;br /&gt;
// verschieben den Schreib/Lesekopf um 'go' (-1, 0, 1)&lt;br /&gt;
// und setzen den neuen Zustand (state) auf 'state'&lt;br /&gt;
//&lt;br /&gt;
// Um effektiv auf den Flash zuzugreifen, ist action_t eine Union,&lt;br /&gt;
// die ein Doppelwort (32 Bit) überlagert.&lt;br /&gt;
typedef union&lt;br /&gt;
{&lt;br /&gt;
    struct&lt;br /&gt;
    {&lt;br /&gt;
        char    wert;&lt;br /&gt;
        char    go;&lt;br /&gt;
        uint8_t state;&lt;br /&gt;
    };&lt;br /&gt;
    uint32_t as_dword;&lt;br /&gt;
} action_t;&lt;br /&gt;
&lt;br /&gt;
// Typ-Definition für eine Default-Aktion:&lt;br /&gt;
// Die Funktion bekommt als Parameter ein char (vom Band gelesener Wert)&lt;br /&gt;
// und einen Zeiger auf das action-Objekt&lt;br /&gt;
typedef void (*job_t)(char, action_t*);&lt;br /&gt;
&lt;br /&gt;
// state_t fasst den Zustand der Turing-Maschine zusammen.&lt;br /&gt;
// Bei einem Beispiel aus der Praxis würde hier wahrscheinlich&lt;br /&gt;
// einiges mehr drin stehen.&lt;br /&gt;
// Die Zustands-Nummer muss für dieses Beispiel&lt;br /&gt;
// nicht gespeichert werden&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
    job_t job;&lt;br /&gt;
} state_t;&lt;br /&gt;
&lt;br /&gt;
// Die Übergangsfunktion:&lt;br /&gt;
// Wenn wir in Zustand Nr. 'state' sind und 'wert' vom Band lesen,&lt;br /&gt;
// dann führen wir die Aktion 'action' aus.&lt;br /&gt;
// Ist kein Übergang definiert, liefert uns die job-Funktion&lt;br /&gt;
// in der state_t Struktur die Aktion.&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  state;&lt;br /&gt;
    char     wert;&lt;br /&gt;
    action_t action;&lt;br /&gt;
} transit_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''main-Funktion, Ausgaberoutine für den PC'''&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt wurde dieses Programm auch dazu verwendet, die Ausgabe der TM zu erzeugen, und zwar auf einem PC. Der Grund, den Code direkt auf einem PC laufen zu lassen, hat mehrere Motivationen: Einerseits soll gezeigt werden, wie Plattformabhängigkeiten aus den Code herausparametrisiert werden können. Wie zu erwarten, sieht der TM-Teil des C-Codes für den PC genauso aus wie der C-Code für AVR. Zum anderen ist die Laufzeitungebung &amp;quot;PC&amp;quot; sowieso vorhanden (jedoch nicht unbedingt die Umgebung, um den C-Code für einen PC zu übersetzen, siehe dazu weiter unten).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
// (Modul-)Globale Variablen, Prototypen&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
int turing (char*);&lt;br /&gt;
int turing_lauf (char*);&lt;br /&gt;
&lt;br /&gt;
// 'size' und 'band0' brauchen wir nur für die Ausgabe&lt;br /&gt;
// die TM braucht davon nichts zu wissen&lt;br /&gt;
size_t size;&lt;br /&gt;
char* band0;&lt;br /&gt;
&lt;br /&gt;
void job_S2_write1 (char, action_t*);&lt;br /&gt;
void job_S3_write0 (char, action_t*);&lt;br /&gt;
&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
// Das Hauptprogramm&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
#ifdef __AVR__&lt;br /&gt;
&lt;br /&gt;
// Auf einem AVR machen wir nichts weiter,&lt;br /&gt;
// als die Turing-Maschine auf die Eingabe &amp;quot;11.&amp;quot; loszulassen&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    return turing_lauf (&amp;quot;11&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define print_band(...)&lt;br /&gt;
&lt;br /&gt;
#else // __AVR__&lt;br /&gt;
&lt;br /&gt;
// Auf dem PC lesen wir die Eingabe von der Kommandozeile,&lt;br /&gt;
// füttern die Turing-Maschine damit und schreiben raus,&lt;br /&gt;
// was die Maschine so werkelt.&lt;br /&gt;
&lt;br /&gt;
// Markiert die aktuelle Position als rot&lt;br /&gt;
// Systemabhängiger Zeilenumbruch&lt;br /&gt;
// Druckbares Zeichen '.' für Stringende (nur für die Ausgabe)&lt;br /&gt;
#define HEAD1_STR &amp;quot;&amp;lt;font color=\&amp;quot;red\&amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
#define HEAD2_STR &amp;quot;&amp;lt;/font&amp;gt;&amp;quot;&lt;br /&gt;
#define NEW_LINE  &amp;quot;&amp;lt;br/&amp;gt;\n&amp;quot;&lt;br /&gt;
#define ENDE_CHAR '.'&lt;br /&gt;
&lt;br /&gt;
int main (int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
    int ok = 0;&lt;br /&gt;
    &lt;br /&gt;
    if (argc &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        printf (&amp;quot;Aufruf: %s 1111...11\n&amp;quot;, argv[0]);&lt;br /&gt;
        return -1;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (1 == argc)        ok = turing_lauf (&amp;quot;&amp;quot;);&lt;br /&gt;
    if (2 == argc)        ok = turing_lauf (argv[1]);&lt;br /&gt;
&lt;br /&gt;
    if (!ok)&lt;br /&gt;
    {&lt;br /&gt;
        printf (&amp;quot;Turing-Maschine ist unvollständig!&amp;quot; NEW_LINE);&lt;br /&gt;
        return -1;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    printf (&amp;quot;Lauf ausgeführt.&amp;quot; NEW_LINE);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schnappschuss des Bands ausdrucken.&lt;br /&gt;
// Ende-Zeichen '\0' muss für die Ausgabe durch ein druckbares Zeichen&lt;br /&gt;
// dargestellt werden.&lt;br /&gt;
void print_band (char *b0, char *b, uint8_t state)&lt;br /&gt;
{&lt;br /&gt;
    size_t i;&lt;br /&gt;
&lt;br /&gt;
    if (TM_FATAL == state)       printf (&amp;quot;Fehler!: &amp;quot;);&lt;br /&gt;
    else if (TM_START == state)  printf (&amp;quot;S:: &amp;quot;);&lt;br /&gt;
    else if (TM_END == state)    printf (&amp;quot;E:: &amp;quot;);&lt;br /&gt;
    else                         printf (&amp;quot;S%d: &amp;quot;, state - TM_START);&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;size; b0++, i++)&lt;br /&gt;
        printf ((b0 == b) ? HEAD1_STR &amp;quot;%c&amp;quot; HEAD2_STR: &amp;quot;%c&amp;quot;, *b0 ?: ENDE_CHAR);&lt;br /&gt;
    &lt;br /&gt;
    printf (NEW_LINE);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif // __AVR__&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Die eigentliche Turing-Maschine'''&lt;br /&gt;
&lt;br /&gt;
Zuerst müssen wir anhand der Eingabe (&amp;lt;tt&amp;gt;input&amp;lt;/tt&amp;gt;) einen hinreichend grossen Speicherbereich besorgen, denn bei der Implementierung wollen wir uns nicht auf eine bestimmte Größe festnageln. Eine Turing-Maschine selbst hat keine Vorstellung von der Größe des Bands, auf dem sie ihre Arbeit tut. Wir verwenden ein dynamisches Array, denn &amp;lt;tt&amp;gt;malloc&amp;lt;/tt&amp;gt; ist der Overkill und wird auch nicht benötigt.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
// Die Turing-Maschine laufen lassen:&lt;br /&gt;
// Dazu müssen wir ihr ein ausreichend langes Band zur&lt;br /&gt;
// Verfüging stellen, da sie die Eingabe &amp;quot;verdoppelt&amp;quot;.&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
int turing_lauf (char *input)&lt;br /&gt;
{&lt;br /&gt;
    // Doppelte Länge und 2 * '\0'&lt;br /&gt;
    size = 2 + 2*strlen (input);&lt;br /&gt;
    char band[size];&lt;br /&gt;
&lt;br /&gt;
    // Wird eigentlich nur auf dem PC gebraucht, aber egal...&lt;br /&gt;
    // Undefnierte Zeichen als '?' anzeigen&lt;br /&gt;
    band0 = band;&lt;br /&gt;
    memset (band, '?', size); &lt;br /&gt;
&lt;br /&gt;
    // Eingabe aufs Band kopieren&lt;br /&gt;
    strcpy (band, input);&lt;br /&gt;
&lt;br /&gt;
    // Und los geht's!&lt;br /&gt;
    return turing (band);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
// Unsere Turing-Maschine (nicht ganz strikt,&lt;br /&gt;
// dafür unsere eigene!&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
action_t the_action;&lt;br /&gt;
action_t *paction = &amp;amp;the_action;&lt;br /&gt;
&lt;br /&gt;
// return 0: Unsere Beschreibung ist unvollständig. Ergänzen/korrigieren!&lt;br /&gt;
// return 1: Alles ok und wir sind fertig.&lt;br /&gt;
int turing (char *band)&lt;br /&gt;
{&lt;br /&gt;
    // Die Zustände (für die wir default-Funktionen geschrieben haben)&lt;br /&gt;
    // In der Praxis wird dieses Array wohl komplett aufgefüllt sein&lt;br /&gt;
    static const state_t states[TM_END - TM_START +1] PROGMEM = {&lt;br /&gt;
        [TM_S2] = { job_S2_write1 },&lt;br /&gt;
        [TM_S3] = { job_S3_write0 }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Die Übergänge:&lt;br /&gt;
    //       { status, wert (lies), {{ wert (schreib), go, status (neu) }}}&lt;br /&gt;
    // oder ausgetextet für bessere Lesbarkeit:&lt;br /&gt;
    //       { .status = **, .wert = **, {{ .wert = **, ...}}}&lt;br /&gt;
    // Falls wir einen Übergang nicht finden, schauen wir ein states[] nach,&lt;br /&gt;
    // was zu tun ist&lt;br /&gt;
    static const transit_t transits[] PROGMEM = {&lt;br /&gt;
        // Start: \0 -&amp;gt; fertig&lt;br /&gt;
        //        1  -&amp;gt; durch \0 ersetzen, rechts, S1&lt;br /&gt;
        { TM_START, '\0', {{ -1,   0, TM_END }}},&lt;br /&gt;
        { TM_START, '1',  {{ '\0', 1, TM_S1  }}},&lt;br /&gt;
&lt;br /&gt;
        // \0 -&amp;gt; rechts, S2 (Ende-Zeichen gefunden)&lt;br /&gt;
        // 1 -&amp;gt; rechts (weitersuchen)&lt;br /&gt;
        { TM_S1, '\0', {{ -1,  1, TM_S2 }}},&lt;br /&gt;
        { TM_S1, '1',  {{ -1,  1, TM_S1 }}},&lt;br /&gt;
&lt;br /&gt;
        // 1 -&amp;gt; rechts (weitersuchen) &lt;br /&gt;
        { TM_S2, '1',  {{ -1,  1, TM_S2 }}},&lt;br /&gt;
&lt;br /&gt;
        // nach links \0 suchen&lt;br /&gt;
        { TM_S4, '\0', {{ -1, -1, TM_S5 }}},&lt;br /&gt;
        { TM_S4, '1',  {{ -1, -1, TM_S4 }}},&lt;br /&gt;
&lt;br /&gt;
        // nach links \0 suchen&lt;br /&gt;
        { TM_S5, '\0', {{ '1', 1, TM_START }}},&lt;br /&gt;
        { TM_S5, '1',  {{ -1, -1, TM_S5 }}}&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Initialisierungen&lt;br /&gt;
    // Auf 'the_action' indirekt zuzugreifen gibt noch kürzeren Code&lt;br /&gt;
    uint8_t state = TM_START;&lt;br /&gt;
    action_t *act = paction;&lt;br /&gt;
&lt;br /&gt;
    // Nur für den PC&lt;br /&gt;
    print_band (band0, band, state);&lt;br /&gt;
&lt;br /&gt;
    // Laufen, bis zum End-Zustand (TM_END)&lt;br /&gt;
    while (TM_END != state)&lt;br /&gt;
    {&lt;br /&gt;
        // Wert vom Band lesen&lt;br /&gt;
        char wert = *band;&lt;br /&gt;
&lt;br /&gt;
        // Nach action in der Tabelle 'transits' suchen.&lt;br /&gt;
        // trans durchläuft das Array transits[]&lt;br /&gt;
        const transit_t *trans = &amp;amp; transits[0];&lt;br /&gt;
        char found = 0;&lt;br /&gt;
        uint8_t i;&lt;br /&gt;
        &lt;br /&gt;
        for (i=0; i &amp;lt; sizeof (transits) / sizeof (transit_t); i++)&lt;br /&gt;
        {&lt;br /&gt;
            // Zustand und Wert (Zeichen) lesen (aus dem Flash bei AVR)&lt;br /&gt;
            uint8_t trans_state = pgm_read_byte (&amp;amp; trans-&amp;gt;state);&lt;br /&gt;
            char    trans_wert  = pgm_read_byte (&amp;amp; trans-&amp;gt;wert);&lt;br /&gt;
            &lt;br /&gt;
            if (state == trans_state &amp;amp;&amp;amp; wert == trans_wert)&lt;br /&gt;
            {&lt;br /&gt;
                // Falls gefunden:&lt;br /&gt;
                // 'the_action' lesen (aus dem Flash bei AVR) und fertig&lt;br /&gt;
                act-&amp;gt;as_dword = pgm_read_dword (&amp;amp; trans-&amp;gt;action.as_dword);&lt;br /&gt;
&lt;br /&gt;
                found = 1;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Nicht gefunden:&lt;br /&gt;
            // Eins weiter in der Tabelle und weitersuchen&lt;br /&gt;
            trans++;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Kein Eintrag gefunden:&lt;br /&gt;
        // Wir führen statt dessen die default-Funktion aus&lt;br /&gt;
        if (!found)&lt;br /&gt;
        {&lt;br /&gt;
            // Funktionszeiger 'job' aus der Struktur lesen&lt;br /&gt;
            job_t job = (job_t) pgm_read_word (&amp;amp; states[state].job);&lt;br /&gt;
&lt;br /&gt;
            // Keine Aktion und keine Funktion definiert: Fataler Fehler!!!&lt;br /&gt;
            if (0 == job)&lt;br /&gt;
                return 0;&lt;br /&gt;
&lt;br /&gt;
            // Funktion ausführen&lt;br /&gt;
            job (wert, act);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        /////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
        // Wir haben die Aktion berechnet:&lt;br /&gt;
        // Wert aufs Band schreiben (falls ungleich -1)&lt;br /&gt;
        if (-1 != act-&amp;gt;wert)&lt;br /&gt;
            *band = act-&amp;gt;wert;&lt;br /&gt;
&lt;br /&gt;
        // Schreib-/Lesezeiger verschieben&lt;br /&gt;
        band += act-&amp;gt;go;&lt;br /&gt;
&lt;br /&gt;
        // Zustand setzen&lt;br /&gt;
        state = act-&amp;gt;state;&lt;br /&gt;
        &lt;br /&gt;
        // Nur für den PC&lt;br /&gt;
        print_band (band0, band, state);&lt;br /&gt;
    } // while (TM_END != state)&lt;br /&gt;
&lt;br /&gt;
    // Alles paletti :-))&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Indirekt aufgerufene Funktionen'''&lt;br /&gt;
&lt;br /&gt;
Folgende Funktionen werden in der Tabelle gemerkt und gegebenenfalls aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Eine Zahl in die Tabelle zu schreiben, die jeweils für eine bestimmte Funktion codiert, würde die Effizienz wieder zunichte machen, weil wieder über eine lange if- oder switch/select Anweisung die richtige Funktion gefunden werden müsste.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen schreiben einen Wert aufs Band ''unabhängig'' von dem gelesenen Wert, denn wir wissen nicht, was auf dem Band so rumsteht ausser der Eingabe.&lt;br /&gt;
&lt;br /&gt;
Natürlich könnten hier auch Funktionen stehen, die Aufgaben erledigen, wenn eine bestimmte Zustandsänderung gemacht werden soll. Als Beispiel wieder eine Menü-Struktur. Von Zustand A kommt man z.B. durch &amp;quot;Abbrechen&amp;quot; zurück nach B, oder durch &amp;quot;Änderungen übernehmen&amp;quot;. Im ersten Falle würde man die Änderungen verwerfen, im zweiten Falle speichern, bzw Funktionen in die Übergangs-Tabelle eintragen, die die jeweilige Aufgabe erledigen. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
// Default-Handler, falls wir für einen Übergang (Transition)&lt;br /&gt;
// keine Aktion definiert haben&lt;br /&gt;
////////////////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
// Nicht alle Argumente werden benötigt,&lt;br /&gt;
// gleichwohl müssen (oder sollen) alle Funktionen den&lt;br /&gt;
// gleichen Prototyp haben. UNUSED verhindert eine GCC-Warnung.&lt;br /&gt;
#define UNUSED __attribute__((unused))&lt;br /&gt;
&lt;br /&gt;
// Schreibt '1', geht nach rechts, wechselt zu S3&lt;br /&gt;
void job_S2_write1 (char wert UNUSED, action_t *ac)&lt;br /&gt;
{&lt;br /&gt;
    ac-&amp;gt;wert  = '1';&lt;br /&gt;
    ac-&amp;gt;go    = 1;&lt;br /&gt;
    ac-&amp;gt;state = TM_S3;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt '\0', geht nach links, wechselt zu S4&lt;br /&gt;
void job_S3_write0 (char wert UNUSED, action_t *ac)&lt;br /&gt;
{&lt;br /&gt;
    ac-&amp;gt;wert  = '\0';&lt;br /&gt;
    ac-&amp;gt;go    = -1;&lt;br /&gt;
    ac-&amp;gt;state = TM_S4;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
'''Verwendung von Labels'''&lt;br /&gt;
&lt;br /&gt;
Will man nicht mit Funktionen arbeiten sondern mit Sprungmarken (Labels), dann geht das ebenfalls. In der betreffenden Struktur/Union macht man ein Feld für das Label:&lt;br /&gt;
 void **label;&lt;br /&gt;
und weist diesem im Initializer das Label zu:&lt;br /&gt;
 .label = &amp;amp;&amp;amp;ein_label, ...&lt;br /&gt;
Das Label springt man indirekt an mit&lt;br /&gt;
 void **alabel = ... // Wert aus Struktur besorgen&lt;br /&gt;
 goto *alabel;&lt;br /&gt;
 label_done:;&lt;br /&gt;
Und das Label selbst definiert man wie gewohnt:&lt;br /&gt;
 ein_label:&lt;br /&gt;
    // mach was (evtl ein ganzer Block)&lt;br /&gt;
 goto label_done; // oder sonst ein Label&lt;br /&gt;
&lt;br /&gt;
Die Strukt muss dann eine lokale, statische Konstante sein (wie die Tabellen im Beispiel auch). Gesprungen werden kann nur innerhalb der gleichen Funktion. Der Unterschied zu Funktionen ist, daß andere Optimierungsstrategien angewandt werden können (common/dead code elimination, common subexpression elimination, kein Overhead durch Funktionsaufruf, andere Register-Verwendung, ...)&lt;br /&gt;
&lt;br /&gt;
'''Compiler'''&lt;br /&gt;
&lt;br /&gt;
Der Code wird übersetzt mit GCC. Falls der GCC ein [[avr-gcc]] ist, wird auf Ausgabefunktionen verzichtet, und wir haben natürlich keine Übergabe-Parameter von der Kommandozeile/Shell. PC-seitig nimmt man einen &amp;quot;normalen&amp;quot; gcc. Bei Linux ist er dabei und für Windows etwa bei MinGW (Minimalistic GNU for Windows).&lt;br /&gt;
&lt;br /&gt;
Natürlich kann der Code auch von jedem AVR-Simulator ausgeführt werden: AVR-Studio, avarice, simulavr, ...&lt;br /&gt;
&lt;br /&gt;
==Bascom (State Machine)==&lt;br /&gt;
Bei Bascom ist es leider nicht möglich, Speicherstrukturen in der Art wie bei GCC zu definieren.&amp;lt;br&amp;gt; &lt;br /&gt;
Es ist weiters dem Bascom schwer auszureden, irgendeinen Wert in Hochkomma als String (mit trailing Zero) zu interpretieren. Deswegen wird, wenn notwendig, &amp;quot;1&amp;quot; als 49 (hex 0x31) und &amp;quot;.&amp;quot; als 46 (hex 0x2E) geschrieben.&amp;lt;br&amp;gt; &lt;br /&gt;
Und es gibt bei Bascom keine &amp;quot;signed bytes&amp;quot;. Dort, wo bei GCC in den Tabellen -1 geschrieben werden kann, ist hier explizit der Wert 255 (hex 0xFF) angegeben.&lt;br /&gt;
&lt;br /&gt;
Die übliche Programm-Einleitung, konkret wurde ein Atmega32 mit 8 MHZ verwendet. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
$baud = 9600&lt;br /&gt;
$hwstack = 32&lt;br /&gt;
$hwstack = 64&lt;br /&gt;
$framesize = 32&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden konstante Werte für die Zustände festgelegt&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
' ---------------------------------------------&lt;br /&gt;
'              states&lt;br /&gt;
' ---------------------------------------------&lt;br /&gt;
Const Tm_fatal = 0&lt;br /&gt;
Const Tm_start = 1&lt;br /&gt;
Const Tm_s1 = 2&lt;br /&gt;
Const Tm_s2 = 3&lt;br /&gt;
Const Tm_s3 = 4&lt;br /&gt;
Const Tm_s4 = 5&lt;br /&gt;
Const Tm_s5 = 6&lt;br /&gt;
Const Tm_end = 7&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Der jeweils aktuelle Zustand wird in den folgenden Feldern geführt&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
' ---------------------------------------------&lt;br /&gt;
'              Actual state&lt;br /&gt;
' ---------------------------------------------&lt;br /&gt;
Dim Actstate As Byte             ' einer der Zustände tm_fatal --&amp;gt; tm_end&lt;br /&gt;
Dim Actvalue As Byte             ' das aktuelle Zeichen vom &amp;quot;Band&amp;quot;&lt;br /&gt;
Dim Actpntr As Word              ' die &amp;quot;Band&amp;quot;-position&lt;br /&gt;
Dim Argument As String * 20      ' das &amp;quot;Band&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
' ---------------------------------------------&lt;br /&gt;
'              Set Input&lt;br /&gt;
' ---------------------------------------------&lt;br /&gt;
 Argument = &amp;quot;11.&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
' ---------------------------------------------&lt;br /&gt;
'              Initial conditions&lt;br /&gt;
' ---------------------------------------------&lt;br /&gt;
 Actstate = Tm_start               ' der Anfangs-&amp;quot;State&amp;quot;&lt;br /&gt;
 Actpntr = Varptr(argument)        ' die &amp;quot;Band&amp;quot;position --&amp;gt; = erstes Zeichen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Hauptschleife wird solange durchlaufen, bis der Zustand entweder =Ende oder =Fehler&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   While Actstate &amp;lt;&amp;gt; Tm_end And Actstate &amp;lt;&amp;gt; Tm_fatal&lt;br /&gt;
      Gosub Showstate              ' Das zeigen der Situation VOR der Transition&lt;br /&gt;
      Actvalue = Inp(actpntr)      ' lesen vom Band&lt;br /&gt;
      Gosub Findsetstate           ' such state &amp;amp; value         &lt;br /&gt;
   Wend&lt;br /&gt;
&lt;br /&gt;
   Print &amp;quot;END&amp;quot;                   &lt;br /&gt;
   Gosub Showstate                 ' Abschluß-Display&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Hilfsroutine, um den Zustand auf dem Terminal darzustellen, dem GCC-Vorbild nachempfunden:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Showstate:&lt;br /&gt;
   Select Case Actstate&lt;br /&gt;
   Case Tm_fatal : Print &amp;quot;F:: &amp;quot;;&lt;br /&gt;
   Case Tm_start : Print &amp;quot;S:: &amp;quot;;&lt;br /&gt;
   Case Tm_s1 : Print &amp;quot;S1: &amp;quot;;&lt;br /&gt;
   Case Tm_s2 : Print &amp;quot;S2: &amp;quot;;&lt;br /&gt;
   Case Tm_s3 : Print &amp;quot;S3: &amp;quot;;&lt;br /&gt;
   Case Tm_s4 : Print &amp;quot;S4: &amp;quot;;&lt;br /&gt;
   Case Tm_s5 : Print &amp;quot;S5: &amp;quot;;&lt;br /&gt;
   Case Tm_end : Print &amp;quot;E:: &amp;quot;;&lt;br /&gt;
   End Select&lt;br /&gt;
   Print Argument&lt;br /&gt;
   Return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hier ist die vordefinierte Tabelle. Um die Lesbarkeit zu gewährleisten, für jeden Zustand / Value eine Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
' ---------------------------------------------&lt;br /&gt;
'  Table&lt;br /&gt;
' ---------------------------------------------&lt;br /&gt;
Statetab:&lt;br /&gt;
'---------stat------val---wrt--go----newsts&lt;br /&gt;
   Data Tm_start , 46 , 255 , 0 , Tm_end&lt;br /&gt;
   Data Tm_start , 49 , 46 , 1 , Tm_s1&lt;br /&gt;
&lt;br /&gt;
   Data Tm_s1 , 46 , 255 , 1 , Tm_s2&lt;br /&gt;
   Data Tm_s1 , 49 , 255 , 1 , Tm_s1&lt;br /&gt;
&lt;br /&gt;
   Data Tm_s2 , 49 , 255 , 1 , Tm_s2&lt;br /&gt;
&lt;br /&gt;
   Data Tm_s4 , 46 , 255 , 255 , Tm_s5&lt;br /&gt;
   Data Tm_s4 , 49 , 255 , 255 , Tm_s4&lt;br /&gt;
&lt;br /&gt;
   Data Tm_s5 , 46 , 49 , 1 , Tm_start&lt;br /&gt;
   Data Tm_s5 , 49 , 255 , 255 , Tm_s5&lt;br /&gt;
&lt;br /&gt;
   Data Tm_fatal                                            ' table delimiter&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Suchen State / Value in der Tabelle &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
' ---------------------------------------------&lt;br /&gt;
'              Find Actual state &amp;amp; Value&lt;br /&gt;
' ---------------------------------------------&lt;br /&gt;
Findsetstate:&lt;br /&gt;
Dim Tabstate As Byte&lt;br /&gt;
Dim Tabvalue As Byte&lt;br /&gt;
Dim Tabwrite As Byte&lt;br /&gt;
Dim Tabgo As Byte&lt;br /&gt;
Dim Tabnewsts As Byte&lt;br /&gt;
&lt;br /&gt;
   Restore Statetab                     ' setzen Tabellen-Beginn&lt;br /&gt;
   Do&lt;br /&gt;
' ----------------- Das Einlesen eines Tabelleneintrages ( = eine &amp;quot;data&amp;quot;-Zeile)&lt;br /&gt;
      Read Tabstate                     ' =status &lt;br /&gt;
      Read Tabvalue                     ' =Value&lt;br /&gt;
      Read Tabwrite                     ' Schreib-Aktion (255 none, 46 (=&amp;quot;.&amp;quot;) oder 49 (=&amp;quot;1&amp;quot;)&lt;br /&gt;
      Read Tabgo                        ' Transport (255: -1 0:keine   1: +1)&lt;br /&gt;
      Read Tabnewsts                    ' der nächste Zustand&lt;br /&gt;
&lt;br /&gt;
      If Tabstate = Actstate And Tabvalue = Actvalue Then        ' Vergleich state &amp;amp; Value&lt;br /&gt;
         Goto Setnewstate                                        ' In der Tabelle gefunden&lt;br /&gt;
      End If&lt;br /&gt;
   Loop Until Tabstate = Tm_fatal&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Es werden solange die Tabelleeinträge gelesen, bis das Tabellenende erreicht wurde oder Zustand &amp;amp; Value gefunden wurden&lt;br /&gt;
Anm: Da &amp;quot;Setnewstate&amp;quot; mit &amp;quot;return&amp;quot; endet, kann hier durchaus ein &amp;quot;goto&amp;quot; verwendet werden. &lt;br /&gt;
&lt;br /&gt;
Hatte die Suche keinen Erfolg, gibt es für zwei Zustände einen Ersatz, der &amp;quot;Value&amp;quot; nicht berücksichtigen muß&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
' not found---&amp;gt; get default:&lt;br /&gt;
   On Actstate Goto Dflt_dmy , Dflt_dmy , Dflt_dmy , Dflt_s2 , Dflt_s3 , Dflt_dmy , Dflt_dmy , Dflt_dmy&lt;br /&gt;
Dflt_dmy:&lt;br /&gt;
   Actstate = Tm_fatal&lt;br /&gt;
   Return&lt;br /&gt;
Dflt_s2:&lt;br /&gt;
   Tabwrite = 49&lt;br /&gt;
   Tabgo = 1&lt;br /&gt;
   Tabnewsts = Tm_s3&lt;br /&gt;
   Goto Setnewstate&lt;br /&gt;
Dflt_s3:&lt;br /&gt;
   Tabwrite = 46&lt;br /&gt;
   Tabgo = 255&lt;br /&gt;
   Tabnewsts = Tm_s4&lt;br /&gt;
   Goto Setnewstate&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Übernahme der Tabellenwerte&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Setnewstate:&lt;br /&gt;
   If Tabwrite &amp;lt; 255 Then&lt;br /&gt;
         Out Actpntr , Tabwrite         ' nur, wenn &amp;quot;.&amp;quot; oder &amp;quot;1&amp;quot; (s.o)&lt;br /&gt;
   End If&lt;br /&gt;
   If Tabgo = 255 Then                  ' negativ kann nicht gefragt werden (s.o), daher explizit&lt;br /&gt;
      Decr Actpntr&lt;br /&gt;
   Else&lt;br /&gt;
      Actpntr = Actpntr + Tabgo         ' 0 oder +1&lt;br /&gt;
   End If&lt;br /&gt;
   Actstate = Tabnewsts                 ' der neue Zustand&lt;br /&gt;
   Return&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Programm muß garnicht auf einen realen Controller übertragen werden, auch im Bascom-Simulator ist ein tadelloser Ablauf möglich. &lt;br /&gt;
&lt;br /&gt;
(Autor: PicNick)&lt;br /&gt;
&lt;br /&gt;
=Siehe auch=&lt;br /&gt;
*[[Codevergleich AVR-Compiler]]&lt;br /&gt;
*[[Avr-gcc]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
*[[Avr]]&lt;br /&gt;
*[[Bascom State Machine Menu]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Praxis]]&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Robotikeinstieg]]&lt;br /&gt;
[[Kategorie:Quellcode C]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;br /&gt;
[[Kategorie:Quellcode Assembler AVR]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User&amp;diff=8675</id>
		<title>Assembler Einführung für Bascom-User</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User&amp;diff=8675"/>
				<updated>2006-09-14T05:55:06Z</updated>
		
		<summary type="html">&lt;p&gt;-tomas-: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Was hier folgt, ist nichts für Profis und Power-User, die mögen weiterblättern. Ich versuche hier, absolute Neueinsteiger nach und nach mit ein paar Grundinformationen zu versorgen. &lt;br /&gt;
&lt;br /&gt;
==Assembler Einführung für Bascom-User==&lt;br /&gt;
&lt;br /&gt;
===Wieso Bascom ?===&lt;br /&gt;
Eine der einfachsten Möglichkeiten, sich an Assembler heranzutasten, ist es, den Bascom-Compiler als Workbench zu benutzen. &lt;br /&gt;
&lt;br /&gt;
Die Vorteile:&lt;br /&gt;
*Das Drumherum mit der richtigen Initialisierung, auch der Perpipherie, kann man bequem von Bascom machen lassen, bis man sich halt auskennt.  &lt;br /&gt;
*Wenn irgendeine Berechnung oder Teil-Funktion nervt oder nicht gleich richtig hinhaut, schreibt man halt doch ein paar Bascom-Statements. &lt;br /&gt;
*fürs Erste reicht die Demo-Version allemal&lt;br /&gt;
&lt;br /&gt;
Die Nachteile:&lt;br /&gt;
*Gott-weiß-wie komfortabel ist der Bascom-Assembler natürlich nicht, aber es reicht. &lt;br /&gt;
*Bei manchen Befehlen ist es nicht klar, ob das ein Assembler oder ein Bascom-Befehl ist. In diesem Fall muß man ein &amp;quot;!&amp;quot; Rufzeichen davor setzen. Man erkennt das aber sofort, denn diese reservierten Bascom-Wort mach er sofort in '''Fettschrift'''. Trotzdem aufpassen !&lt;br /&gt;
&lt;br /&gt;
==Ein Grund-Programm==&lt;br /&gt;
Nicht lachen, auch das ist ein Bascom-Programm:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$asm&lt;br /&gt;
   &lt;br /&gt;
$end Asm&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das Programm macht natürlich überhaupt nix. Aber durch die paar Zeilen hat Bascom alle &lt;br /&gt;
[[AVR_Assembler_Einf%C3%BChrung#Struktur_eines_AVR-Maschinenprogrammes|notwendigen Initialisierungen]] schon erledigt und wir brauchen uns um nichts zu kümmern.&lt;br /&gt;
Zwischen &amp;quot;$asm&amp;quot; und &amp;quot;$end asm&amp;quot; kann man nun nach Herzenslust irgendwas Assemblermäßiges reinschreiben und mit dem Simulator rumprobieren. &lt;br /&gt;
&lt;br /&gt;
Auch &amp;quot;REGFILE&amp;quot; müßte man nicht hinschreiben, dann gilt eben das, was man in &amp;quot;OPTIONS/COMPILER/CHIP&amp;quot; eingestellt hat.&lt;br /&gt;
&lt;br /&gt;
==Der Zentral-Prozessor (CPU)==&lt;br /&gt;
Das ist der Kollege, dem man mit &amp;quot;Assembler-Instruktionen&amp;quot; davon überzeugen muß, irgendwas zu tun. Ohne den läuft garnix. Der hat als Hilfe einen &amp;quot;'''Befehlszähler'''&amp;quot; (PC), der immer auf den nächsten Befehl zeigt, der drankommt. Und dann hat er noch eine Reihe &amp;quot;'''Register'''&amp;quot;, das sind kleine Zwischenspeicher, mit denen er arbeiten kann. Die heissen einfach &amp;quot;R0&amp;quot;, &amp;quot;R1&amp;quot;,....&amp;quot;R31&amp;quot;, also 32 Stück, in jedes paßt genau ein Byte, und ein Byte, das wissen wir, besteht wiederum aus 8 Bits.&lt;br /&gt;
&lt;br /&gt;
[[Atmel_Controller_Mega16_und_Mega32#CPU|Hübsche Zeichnungen von Atmega32-CPU und einige weiterführende Information]] &lt;br /&gt;
&lt;br /&gt;
===Registerverwendung===&lt;br /&gt;
An sich sind wir zwischen &amp;quot;$asm&amp;quot; und &amp;quot;$end asm&amp;quot; alleiniger Herrscher über den Mikrokontroller. Damit aber auch dem Bascom ein bißchen was zu Leben bleibt, sollten wir einige Register entweder in Ruhe lassen oder erst sichern und dann wieder herstellen. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 R4, R5         ' die beiden verwendet Bascom für temporäre Sachen. &lt;br /&gt;
 R6             ' da speichert er einige Schalter (Bits)&lt;br /&gt;
 R8, R9         ' verwendet es für  &amp;quot;READ&amp;quot; und &amp;quot;RESTORE&amp;quot; etc. &lt;br /&gt;
                ' haben wir sowas aber garnicht, ist es egal&lt;br /&gt;
 R28, R29       ' braucht Bascom aber nur, wenn wir Funktionen und Subs aufrufen. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen brauchen wir uns aber darum nicht zu kümmern, es wird nix davon gebraucht. Ich wollt' es nur gesagt haben.&lt;br /&gt;
&lt;br /&gt;
===Daten-Transfer Operationen===&lt;br /&gt;
Bevor wir mit diesen Registern irgendetwas ausprobieren können, müssen wir erstmal gezielt bestimmte Werte reinschreiben können. Sowas heißt eben &amp;quot;Transfer&amp;quot;. Da wir ja erst am Anfang sind, reicht uns zum Beispiel:&lt;br /&gt;
 LDI   R24, 14&lt;br /&gt;
Damit wird in das Register R24 der Binärwert von &amp;quot;14&amp;quot; reingestellt, das sind die Bits &amp;quot;00001110&amp;quot;. Der maximale Wert, da es ja nur ein Byte ist, wäre &amp;quot;255&amp;quot;, also &amp;quot;11111111&amp;quot;. &lt;br /&gt;
Für den Befehl &amp;quot;LDI&amp;quot; können wir übrigens leider nur die Register R16 - R31 setzen, das ist so eine Einschränkung von wegen &amp;quot;RISC&amp;quot; Architektur.&lt;br /&gt;
 MOV   R3, R24&lt;br /&gt;
Deswegen auch der zweite Befehl &amp;quot;MOV&amp;quot;, damit wird im Beispiel der Inhalt von R24 in das Register R3 kopiert. Somit können wir mit maximal zwei Befehlen also jeder beliebige Register von R0 bis R31 mit beliebigen Werten laden. &lt;br /&gt;
Natürlich gibt es noch eine Menge mehr an Transferbefehlen, aber Listen von Assembler-Befehlen gibt es schon genug, da brauchen wir hier nicht auch noch eine.&lt;br /&gt;
&lt;br /&gt;
===Arithmetisch-Logische Operationen===&lt;br /&gt;
Laden wir mal zwei Register:&lt;br /&gt;
 LDI   R25, 17&lt;br /&gt;
 LDI   R24, 14&lt;br /&gt;
&lt;br /&gt;
Und jetzt die Grund-Befehle, Varianten später:&lt;br /&gt;
*Arithmetisch&lt;br /&gt;
 ADD   R25, R24       addieren      R25 + R24, Ergebnis nach R25&lt;br /&gt;
 !SUB   R25, R24       subtrahieren&lt;br /&gt;
*Logisch &lt;br /&gt;
 !AND   R25, R24       &amp;quot;UND&amp;quot;&lt;br /&gt;
 !OR    R25, R24       &amp;quot;ODER&amp;quot;&lt;br /&gt;
 EOR   R25, R24       &amp;quot;Exklusiv-ODER&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis steht immer in Operand-1&lt;br /&gt;
&lt;br /&gt;
===Gleich mal ausprobieren===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$asm&lt;br /&gt;
 LDI   R25, 17        ' Laden&lt;br /&gt;
 LDI   R24, 14        ' Laden&lt;br /&gt;
 ADD   R25, R24       'addieren  17 + 14, Ergebnis in R25&lt;br /&gt;
&lt;br /&gt;
 LDI   R25, 17        'Nachladen, da R25 durch &amp;quot;ADD&amp;quot; ja verändert wurde&lt;br /&gt;
 !SUB   R25, R24       'subtrahieren  17 - 14&lt;br /&gt;
&lt;br /&gt;
 LDI   R25, 17        ' Laden&lt;br /&gt;
 LDI   R24, 14        ' Laden&lt;br /&gt;
 !AND   R25, R24       ' Es kommt überall dort &amp;quot;1&amp;quot; raus, wo sowohl r25 als auch R24 eine 1 haben&lt;br /&gt;
 &lt;br /&gt;
 LDI   R25, 17        ' Laden&lt;br /&gt;
 LDI   R24, 14        ' Laden&lt;br /&gt;
 !OR    R25, R24       ' Es kommt überall dort &amp;quot;1&amp;quot; raus, wo r25 oder R24 eine 1 haben&lt;br /&gt;
                      '  (ODER BEIDE !)&lt;br /&gt;
&lt;br /&gt;
 LDI   R25, 17        ' Laden&lt;br /&gt;
 LDI   R24, 14        ' Laden&lt;br /&gt;
 EOR   R25, R24       ' Es kommt überall dort &amp;quot;1&amp;quot; raus, wo ENTWEDER  r25 oder R24 eine 1 haben&lt;br /&gt;
                      '  (ABER NICHT BEIDE !)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$end Asm&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Probieren ist das am besten mit dem Simulator. (Register-Fenster öffnen und Einzelschritte)&lt;br /&gt;
&lt;br /&gt;
===Ergebnis prüfen===&lt;br /&gt;
Normalerweise ist es ja nicht so, daß vor solchen Operationen die Rechenwerte direkt geladen werden, sondern die kommen ja von irgendwo aussen her. Und da muß man ja dann anders reagieren, je nachdem, ob die Werte gleich waren, ob r25 größer oder kleiner als r24 war, und so weiter. &lt;br /&gt;
&lt;br /&gt;
Da helfen die &amp;quot;Flags&amp;quot; im '''Status-Register''' (SREG). Das ist zwar auch ein normales Byte, nur haben die einzelnen Bits darin eine spezielle Bedeutung und geben eben nähere Auskunft über die gerade abgelaufenen Operation. Nur das Wichtigste:&lt;br /&gt;
*ZERO-Bit  Es wird automatisch gesetzt, wenn das Ergebnis genau NULL ergeben hat.  &lt;br /&gt;
*CARRY-Bit Es wird automatisch gesetzt, wenn es einen &amp;quot;Übertrag&amp;quot; gegeben hat&lt;br /&gt;
&lt;br /&gt;
Man kann diese (und noch andere) Flags sehen, wenn man im Simulator auf &amp;quot;µP&amp;quot; drückt. &lt;br /&gt;
 Z = ZERO&lt;br /&gt;
 C = CARRY&lt;br /&gt;
'''Beispiele:'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LDI   R25, 17       &lt;br /&gt;
LDI   R24, 14       &lt;br /&gt;
!SUB   R25, R24       &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Zero &amp;amp; Carry sind nicht gesetzt, denn das Ergebnis ist ungleich NULL, und &amp;quot;17&amp;quot; ist außerdem größer als &amp;quot;14&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LDI   R25, 17       &lt;br /&gt;
LDI   R24, 17       &lt;br /&gt;
!SUB   R25, R24       &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt ist Zero gesetzt, denn das Ergebnis ist gleich NULL&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LDI   R25, 12       &lt;br /&gt;
LDI   R24, 44       &lt;br /&gt;
!SUB   R25, R24       &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt ist das Carry-Bit gesetzt, denn &amp;quot;12&amp;quot; ist ja kleiner als &amp;quot;44&amp;quot;, das Ergebnis ist also negativ, und ein &amp;quot;Übertrag&amp;quot; ist auch aufgetreten.&lt;br /&gt;
&lt;br /&gt;
===Vergleichen===&lt;br /&gt;
&amp;quot;Vergleichen&amp;quot; ist für die ALU (Recheneinheit) das Gleiche wie Subtrahieren (SUB), nur daß das eigentliche Rechenergebnis nirgends hingeschrieben wird und NUR DIE FLAGS gesetzt werden. &lt;br /&gt;
 CP  R25, R24       &lt;br /&gt;
&lt;br /&gt;
===Verzweigen===&lt;br /&gt;
Wir haben ja gesagt, es wird verglichen, damit der Rechner je nach Vergleichs- der Rechenergebnis was anderes tut. &amp;quot;Was anderes tun&amp;quot; heißt anderer Code, also muß der &amp;quot;Befehlszähler&amp;quot; einen anderen Wert bekommen, damit der Programmablauf dort fortgesetzt wird. Dazu gibt es natürlich die &amp;quot;unbedingten&amp;quot; Varianten&lt;br /&gt;
 JMP  Zieladresse  ' oder&lt;br /&gt;
 RJMP Zieladresse  ' das nimmt man, wenn das Ziel in der Nähe ist&lt;br /&gt;
Oder eben die &amp;quot;Verzweigung unter bestimmten Bedingungen&amp;quot; (conditional branch) &lt;br /&gt;
 BRxxx Zieladresse &lt;br /&gt;
Für &amp;quot;xxx&amp;quot; (Bedingung) gibt es nun eine ganze Reihe Möglichkeiten. Es gibt im Prinzip für jedes Bit im Status-Register (s.o) eine Abfrage &amp;quot;wenn gesetzt&amp;quot; und &amp;quot;wenn nicht gesetzt&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Die wohl wichtigsten sind die Möglichkeiten, die sich aus dem &amp;quot;ZERO&amp;quot;- und dem &amp;quot;CARRY&amp;quot;-Flag ergeben:&lt;br /&gt;
 BREQ Zieladresse   ' Verzweigen, wenn &amp;quot;GLEICH&amp;quot;  (equal)                      Zero  = 1&lt;br /&gt;
 BRNE Zieladresse   ' Verzweigen, wenn &amp;quot;NICHT GLEICH&amp;quot;  (not equal)            Zero  = 0&lt;br /&gt;
 BRLO Zieladresse   ' Verzweigen, wenn &amp;quot;KLEINER&amp;quot;  (lower)                     Carry = 1&lt;br /&gt;
 BRSH Zieladresse   ' Verzweigen, wenn &amp;quot;GLEICH ODER GRÖSSER&amp;quot; (same or higher) Carry = 0&lt;br /&gt;
Und, die Überraschung, ausgerechnet sowas Häufiges wie &lt;br /&gt;
 Verzweigen, wenn &amp;quot;GRÖSSER&amp;quot;&lt;br /&gt;
gibt's überhaupt nicht. Nun, dazu müßten ja eigentlich zwei Flags abgefragt werden. &amp;quot;Größer&amp;quot; heißt nämlich CARRY = 0 UND ZERO = 0. Und das ist in der &amp;quot;RISC&amp;quot; Welt nicht drin, da wird gespart.&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
Lieber gleich ein Beispiel zum Ausprobieren und Festigen, das war ja doch etwas gebündelt. Aber davor gleich noch eins drauf: Eine &amp;quot;Zieladresse&amp;quot; ist der (im ganzen Programm) eindeutige Name eines Befehls (ein &amp;quot;Label&amp;quot;), der in der Zeile ganz links beginnt und mit Doppelpunkt abgeschlossen wird&lt;br /&gt;
&lt;br /&gt;
====Flußdiagramm====&lt;br /&gt;
*Theoretisch sieht das ja so aus:&lt;br /&gt;
[[Bild:Compare1.png]]&lt;br /&gt;
&lt;br /&gt;
*Da es aber keiner Programmierspache möglich ist, alternativen Code nebeneinander zu schreiben, muß dieser Teil auf &amp;quot;Spaghetti&amp;quot;-Code umstrukturiert werden. &lt;br /&gt;
&lt;br /&gt;
&amp;quot;Hochsprachen&amp;quot; machen das versteckt im Maschinencode, beim Assembler müssen wir selbst machen. Und natürlich auch &amp;quot;GOTO&amp;quot; (=JMP) verwenden, ein sonst in allen Büchern als &amp;quot;no, no&amp;quot; (=pfui) beschriebener Befehl.  &lt;br /&gt;
====Die Praxis====&lt;br /&gt;
&lt;br /&gt;
[[Bild:Compare2.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Programm_Beginn:                            ' das ist zum Beispiel gleich ein &amp;quot;Label&amp;quot;&lt;br /&gt;
          LDI         R25, 12       ' R25 = 12&lt;br /&gt;
          LDI         R24, 44       ' R24 = 44&lt;br /&gt;
'--------------------------------------&lt;br /&gt;
'  nun der Vergleich   &lt;br /&gt;
'--------------------------------------&lt;br /&gt;
          CP          R25, R24       &lt;br /&gt;
          BREQ        Label_1    ' Verzweigen nach &amp;quot;Ziel&amp;quot;, wenn R25 = R24&lt;br /&gt;
&lt;br /&gt;
          LDI         R16, 1      ' das machen wir (zum Beispiel), wenn R25 NICHT= r24 ist &lt;br /&gt;
          RJMP        Label_2    'wir müssen unbedingt springen, sonst laufen wir ja &lt;br /&gt;
                                         ' in den Zweig &amp;quot;ist_gleich&amp;quot;  rein&lt;br /&gt;
Label_1:&lt;br /&gt;
          LDI         R16, 0      ' das machen wir (zum Beispiel), wenn R25 = r24 ist &lt;br /&gt;
&lt;br /&gt;
'----------------------------     ' da treffen wir uns wieder&lt;br /&gt;
Label_2:            &lt;br /&gt;
         da geht er wieder gemeinsam weiter &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ich kann nur dringend empfehlen, sich mit diesem Beispiel zu beschäftigen und auch mit anderen Werten rumzuprobieren, das &amp;quot;bedingte Verzweigen&amp;quot; in allen Varianten ist das A und O der Programmiererei, beim Assembler eben auch ein bißchen verschärft.&lt;br /&gt;
&lt;br /&gt;
====Eine Alternative: Bedingtes &amp;quot;Skip&amp;quot;====&lt;br /&gt;
Was der AVR noch anbietet, ist eine Reihe von &amp;quot;SKIP IF&amp;quot; Befehlen. Für unseren Registervergleich gibt es aber nur den &lt;br /&gt;
 CPSE Register, Register &lt;br /&gt;
Befehl. Er bedeutet:&lt;br /&gt;
 &amp;quot;Vergleiche die Register, und wenn die Inhalte gleich sind, überspringe den nächsten Befehl&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Das wird uns das Herumspringen und das Verwenden von Labeln erspart. Allerdings kann immer nur EIN Befehl übersprungen werden&lt;br /&gt;
&lt;br /&gt;
[[Bild:skip.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
eine Besonderheit hat der Befehl noch: Da er ja Vergleich und Bedingungsabfrage in Einem ist, werden auch keine Flags im Statusregister (SREG) verändert. Das ist praktisch, wenn man diese Flags durch eine andere Operation vorher gesetzt hat, und sie über diesen Vergleichs + Sprung - Befehl  darüber-retten will. Das ist aber im Moment schon etwas fortgeschritten.&lt;br /&gt;
&lt;br /&gt;
==Kurze Zusammenfassung==&lt;br /&gt;
*Wir können also beliebige Register mit beliebigen Werten laden, &lt;br /&gt;
*Wir können mit diesen Werten rechnen oder sie vergleichen&lt;br /&gt;
*Und je nach Vergleichs- oder Rechenergebnis unterschiedlichen Code durchlaufen. &lt;br /&gt;
&lt;br /&gt;
*Man könnte aber auch ein paar Lehren daraus ziehen: &lt;br /&gt;
**die Register R16 - R31 braucht man unter Umständen für Zwischenschritte, um Werte in die Register R0 - R15 laden zu können. Man sollte also diese Register nicht zu schnell fest belegen und vollräumen, damit man dafür noch Spielraum behält.&lt;br /&gt;
**Auch doch recht simple IF .. ELSE Konstrukte können ein gewisses vorher überlegtes Konzept brauchen, sonst verliert man schnell den Überblick. Ein Blatt Papier und ein Bleistift sind also recht hilfreich. Assembler schreibt man nicht einfach in den Bildschirm rein.&lt;br /&gt;
&lt;br /&gt;
==Schleifen==&lt;br /&gt;
Eigentlich ist das ja nichts speziell Assembler-spezifisches, aber was soll's. &lt;br /&gt;
===Flußdiagramme===&lt;br /&gt;
Es gibt zwei Grundmuster für Schleifen (Befehlswiederholungen).&lt;br /&gt;
&lt;br /&gt;
*WHILE  &amp;quot;solange ''Bedingung'' erfüllt ist, mache was&amp;quot;&lt;br /&gt;
[[Bild:While.png]]&lt;br /&gt;
&lt;br /&gt;
*DO...LOOP WHILE  &amp;quot;mache was, solange ''Bedingung'' erfüllt ist&amp;quot;&lt;br /&gt;
[[Bild:DoWhile.png]]&lt;br /&gt;
&lt;br /&gt;
Der Unterschied ist wichtig: Bei &amp;quot;WHILE&amp;quot; wird nur was gemacht, wenn die Bedingung schon zutrifft, Bei &amp;quot;DO..WHILE&amp;quot; werden die Befehle auf jeden Fall wenigstens einmal ausgeführt, erst dann wird gecheckt, ob wiederholt werden soll.&lt;br /&gt;
&lt;br /&gt;
===Praxis===&lt;br /&gt;
Theoretisch sieht das ja gut aus, und mit Hochsprachen kann man das auch meist so formulieren. Beim Assembler geht das aber nur so schön übersichtlich, wenn man nur eine einzelne Bedingung hat. Eine einfache Zähl-Schleife in der &amp;quot;WHILE&amp;quot; Version:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
$asm&lt;br /&gt;
   LDI   r25, 0         ' R25 = 0&lt;br /&gt;
   LDI   r24, 1         ' R24 = 1&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12           ' Der Befehl ist neu: vergleiche R25 mit dem festen Wert &amp;quot;12&amp;quot; &lt;br /&gt;
   BREQ  SchleifenAusgang  ' Wenn R25 = 12, verlassen wir die Schleife&lt;br /&gt;
   ADD   R25, R24          ' auf R25 den Wert von R24 draufaddieren &lt;br /&gt;
   RJMP  SchleifenBeginn   ' und wieder rauf zur Prüfung&lt;br /&gt;
SchleifenAusgang:&lt;br /&gt;
   ...&lt;br /&gt;
$end Asm&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Was geschieht, ist klar: R25 beginnt mit Null. Wenn der R25 NICHT= &amp;quot;12&amp;quot;, addieren wir &amp;quot;1&amp;quot; auf R25 und wiederholen das Ganze. Wenn R25 = &amp;quot;12&amp;quot;, verlassen wir die Schleife. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nehmen wir aber an, wir hätten zwei Bedingungen (es geht hier nicht um Sinn oder Unsinn der Abfrage):&lt;br /&gt;
*WHILE R25 NICHT= &amp;quot;12  UND R24 = &amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12           ' Der Befehl ist neu: vergleiche R25 mit dem festen Wert &amp;quot;12&amp;quot; &lt;br /&gt;
   BREQ  SchleifenAusgang  ' Wenn R25 = 12, verlassen wir die Schleife&lt;br /&gt;
   CPI   R24, 1            ' s.o&lt;br /&gt;
   BRNE  SchleifenAusgang  ' Wenn R24 NICHT= 1, verlassen wir die Schleife&lt;br /&gt;
   ADD   R25, R24          ' auf R25 den Wert von R24 draufaddieren &lt;br /&gt;
   RJMP  SchleifenBeginn   ' und wieder rauf zur Prüfung&lt;br /&gt;
SchleifenAusgang:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*WHILE R25 NICHT= &amp;quot;12  ODER R24 &amp;lt; R25&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 &lt;br /&gt;
SchleifenBody:&lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  SchleifenBody&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Wenn wir da nicht im Kommentar dazuschreiben, worum es geht, kennt sich ein Fremder erst nach einiger Überlegung aus.&lt;br /&gt;
&lt;br /&gt;
===Tips===&lt;br /&gt;
Mehrere Bedingungen in eine UND-ODER Beziehung sind immer fehleranfällig und leicht unübersichtlich&lt;br /&gt;
&lt;br /&gt;
*Als Erstes immer die RICHTIGE (und am besten verständliche) Lösung suchen, und erst dann durch Umformungen die &amp;quot;SCHÖNE&amp;quot; Lösung. &lt;br /&gt;
&lt;br /&gt;
*Also nochmal das obige &amp;quot;ODER&amp;quot; Beispiel, erst in der vollen Grundform&lt;br /&gt;
&lt;br /&gt;
 WHILE  ( R25 NICHT= &amp;quot;12 ) ODER  ( R24 &amp;lt; R25 )&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12           ' R25 &amp;lt;=&amp;gt; 12&lt;br /&gt;
   BREQ  R25_ist_12&lt;br /&gt;
   JMP   R25_ist_nicht_12&lt;br /&gt;
&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   ADD   R25, R24          ' der &amp;quot;BODY&amp;quot; steht ja fest &lt;br /&gt;
   RJMP  SchleifenBeginn   ' das ist auch sicher&lt;br /&gt;
&lt;br /&gt;
SchleifenAusgang:          ' ausgang gibt es (eigentlich) immer&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das fehlt was ? Ja, denn jetzt erst sollten wir die Ziele auch hinschreiben&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1. Wir machen den &amp;quot;body&amp;quot; immer, wenn   r25 nicht gleich 12&lt;br /&gt;
&lt;br /&gt;
also schreiben wir das hin&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12           ' &lt;br /&gt;
   BREQ  R25_ist_12&lt;br /&gt;
   JMP   R25_ist_nicht_12  ' abgehakt&lt;br /&gt;
&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25&lt;br /&gt;
&lt;br /&gt;
R25_ist_nicht_12:          ' &lt;br /&gt;
   ADD   R25, R24          ' &lt;br /&gt;
   RJMP  SchleifenBeginn   ' &lt;br /&gt;
&lt;br /&gt;
SchleifenAusgang:          ' &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Wir machen den &amp;quot;body&amp;quot; immer, wenn   r24 kleiner als r25&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12&lt;br /&gt;
   JMP   R25_ist_nicht_12           ' abgehakt&lt;br /&gt;
&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        ' abgehakt&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25  ' &lt;br /&gt;
&lt;br /&gt;
r24_ist_kleiner_r25:&lt;br /&gt;
R25_ist_nicht_12:           &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn    &lt;br /&gt;
&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Anmerkung: wir können an der selben Stelle beliebig viele Label vergeben&lt;br /&gt;
&lt;br /&gt;
3. Was ist, wenn r25 = 12 ?  dann müssen wir die zweite Bedingung prüfen (ist ja ein ODER) &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 ' abgehakt&lt;br /&gt;
   JMP   R25_ist_nicht_12           ' abgehakt&lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        ' abgehakt&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25  ' &lt;br /&gt;
&lt;br /&gt;
r24_ist_kleiner_r25:&lt;br /&gt;
R25_ist_nicht_12:           &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn    &lt;br /&gt;
&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
4. Bleibt nurmehr &amp;quot;r24 ist nicht kleiner r25&amp;quot;.  Da geht's offenbar dann hin, wenn KEINE der Bedingungen erfüllt ist, also: raus aus der Schleife&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 ' abgehakt &lt;br /&gt;
   JMP   R25_ist_nicht_12           ' abgehakt &lt;br /&gt;
&lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        ' abgehakt&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25  ' abgehakt&lt;br /&gt;
&lt;br /&gt;
r24_ist_kleiner_r25:&lt;br /&gt;
R25_ist_nicht_12:           &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
  &lt;br /&gt;
r24_ist_nicht_kleiner_r25:&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt ist das Ganze zwar nicht elegant, aber richtig und leicht nachvollziehbar.&lt;br /&gt;
&lt;br /&gt;
Wenn der Sprungbefehl und das Ziel unmittelbar hintereinander stehen, können wir uns den Sprung sparen. Also bauen wir etwas um, damit das auch so ist:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 '&lt;br /&gt;
   JMP   R25_ist_nicht_12           ' steht jetzt direkt dahinter&lt;br /&gt;
&lt;br /&gt;
r24_ist_kleiner_r25:                'den ganzen Block raufgeschoben &lt;br /&gt;
R25_ist_nicht_12:           &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
&lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        '&lt;br /&gt;
   JMP   r24_ist_nicht_kleiner_r25  ' steht jetzt direkt dahinter&lt;br /&gt;
 &lt;br /&gt;
r24_ist_nicht_kleiner_r25:&lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und kürzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 &lt;br /&gt;
r24_ist_kleiner_r25:      &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        &lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ob das nun ein &amp;quot;WHILE&amp;quot; oder ein &amp;quot;DO..WHILE&amp;quot;  wird, hängt nurmehr davon ab, wo wir zu Beginn in die Befehlsfolge reinspringen. &lt;br /&gt;
*Von oben weg, wie es dort steht, ist es eine &amp;quot;WHILE&amp;quot; Schleife&lt;br /&gt;
*Eine &amp;quot;DO..WHILE&amp;quot; Schleife (Bedingung am Ende prüfen) wird es, wenn wir zuerst mit dem &amp;quot;Body&amp;quot; beginnen. Also &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   JMP   r24_ist_kleiner_r25     ' Erst die Aktion, DANN die Bedingung prüfen &lt;br /&gt;
SchleifenBeginn:&lt;br /&gt;
   CPI   R25, 12            &lt;br /&gt;
   BREQ  R25_ist_12                 &lt;br /&gt;
r24_ist_kleiner_r25:      &lt;br /&gt;
   ADD   R25, R24           &lt;br /&gt;
   RJMP  SchleifenBeginn  &lt;br /&gt;
R25_ist_12:&lt;br /&gt;
   CP    R24, R25&lt;br /&gt;
   BRLO  r24_ist_kleiner_r25        &lt;br /&gt;
SchleifenAusgang:           &lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist doch praktisch ? &lt;br /&gt;
&lt;br /&gt;
Assembler-mäßig ist das nun ok und erträglich. Aber mit dem theoretischen WHILE-Flußdiagramm hat das nun nicht mehr viel gemeinsam.&lt;br /&gt;
&lt;br /&gt;
==Weg vom Simulator auf den µC==&lt;br /&gt;
Jetzt wird's Zeit, die ersten Schritte in die AVR-Realität zu machen, immer Simulator ist ja langweilig, fast so, als würden wir im Trockenen schwimmen lernen müssen. &lt;br /&gt;
&lt;br /&gt;
Bis jetzt haben wir vom Bascom ja nur die äussere Programmhülle verwendet, jetzt soll er doch auch wirklich was tun. &lt;br /&gt;
&lt;br /&gt;
===Datenaustausch mit BasCom===&lt;br /&gt;
Das funktioniert über Datenfelder, die wir irgendwie definieren müssen. Weil's so einfach ist, lassen wir das erstmal Bascom übernehmen. Das ist kein Rückschritt auf dem Weg zum Assemblerprogrammierer, denn das Definieren von Daten ist ja nichts anderes, als Felderen im SRAM (also im eigentlichen Arbeitsspeicher) Namen zuzuweisen. Über den Namen kann man diese Felder dann auch im Assembler ansprechen. &lt;br /&gt;
&lt;br /&gt;
*'''Bascom--&amp;gt;Assembler'''&lt;br /&gt;
Damit Bascom ein Feld für uns definiert, sagen wir einfach (aber außerhalb der &amp;quot;$asm&amp;quot; / &amp;quot;$end asm&amp;quot; Bereiches) &lt;br /&gt;
 DIM Meinfeld AS BYTE &lt;br /&gt;
Im Bascom, das weiß der Leser vielleicht ja schon, kann man da Werte reinschreiben, so wie wir das mit dem &amp;quot;LDI&amp;quot; Befehl bei Registern gemacht haben&lt;br /&gt;
  Meinfeld = 85 &lt;br /&gt;
Jetzt steht an irgendeiner Speicherstelle (egal wo, wir sprechen es eh' nur über den Namen an) der Binärwert von 85  (Hexadezimal &amp;amp;H55 oder in Bits &amp;amp;B01010101).&lt;br /&gt;
Dieses Feld können wir aber nun auch mit dem Assembler lesen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte           ' definition&lt;br /&gt;
&lt;br /&gt;
   Meinfeld = 85               'Wertzuweisung&lt;br /&gt;
&lt;br /&gt;
   $asm&lt;br /&gt;
   lds   r24, {Meinfeld}        '(das sind zwei geschwungene Klammern)&lt;br /&gt;
                                ' das ist wieder ein neuer Befehl: &amp;quot;LDS&amp;quot;&lt;br /&gt;
   $end Asm&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;LDS&amp;quot; lädt in ein beliebiges Register den Wert von der Speicherstelle, die &amp;quot;Meinfeld&amp;quot; heißt. &lt;br /&gt;
&lt;br /&gt;
*'''Assembler--&amp;gt;Bascom'''&lt;br /&gt;
Das geht auch umgekehrt. Wenn wir den Bascomteil noch etwas vervollständigen&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000               ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                     ' die Baudrate für das Terminal&lt;br /&gt;
                                 ' dadurch macht Bascom alle Einstellungen, die wir für das &lt;br /&gt;
                                 ' Terminal brauchen&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte      ' definition&lt;br /&gt;
&lt;br /&gt;
   $asm&lt;br /&gt;
   LDI   R24, 85&lt;br /&gt;
   STS   {Meinfeld}, R24  ' &amp;quot;STS&amp;quot; ist das Gegenstück zu &amp;quot;LDS&amp;quot;, also vom Register zum Speicher&lt;br /&gt;
   $end Asm&lt;br /&gt;
&lt;br /&gt;
   PRINT  str(Meinfeld)   ' Und schon gibt uns Bascom den Wert (dezimal) auf dem Terminal aus&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Das ist der Vorteil den wir haben, wenn wir Assembler mit Bascom anfangen und nicht mit einem &amp;quot;richtigen&amp;quot; Assembler wie das AVR-Studio. Denn das Gefummel mit der Terminalausgabe erledigt alles Bascom, sonst müßten wir es selbst erst wo abschreiben oder lernen, bis wir auch nur einen Pieps auf dem Terminal sehen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Wenn es interessiert: Das, was wir zuletzt im Assembler geschrieben haben, ist auch genau das, was Bascom an Maschinencode produziert, wenn wir &lt;br /&gt;
 Meinfeld = 85&lt;br /&gt;
hinschreiben&lt;br /&gt;
&lt;br /&gt;
*'''Bascom--&amp;gt;Assembler--&amp;gt;Bascom'''&lt;br /&gt;
Noch immer können wir nur mit fest einprogrammierten Werten arbeiten, d.h. für was anderes als &amp;quot;85&amp;quot; müssen wir ändern, übersetzen und brennen.&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun versuchen, etwas über das Terminal einzugeben, bearbeiten und dann wieder ausgeben. &lt;br /&gt;
&lt;br /&gt;
'''Flußdiagramm'''&lt;br /&gt;
&lt;br /&gt;
[[Bild:InOut.png]]&lt;br /&gt;
&lt;br /&gt;
Den Input lassen wir Bascom übernehmen. INKEY() holt einen Wert von der Tastatur. Er wartet aber nicht, bis was gedrückt wird, sondern gibt einfach NULL aus, wenn nix da ist. &lt;br /&gt;
Wir arbeiten also nur etwas, wenn ein Wert &amp;gt; NULL da ist. &lt;br /&gt;
&lt;br /&gt;
*Zuerst übernehmen wir nur den Part &amp;quot;Bearbeitung&amp;quot; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000               ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                     ' die Baudrate für das Terminal&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte             &lt;br /&gt;
DO&lt;br /&gt;
   Meinfeld = INKEY()            '&amp;quot;EINGABE&amp;quot;&lt;br /&gt;
   IF Meinfeld &amp;lt;&amp;gt; 0 THEN         '&amp;quot;BEDINGUNG&amp;quot;&lt;br /&gt;
       $asm                      '&amp;quot;BEARBEITUNG&amp;quot;&lt;br /&gt;
       lds   r24, {Meinfeld}&lt;br /&gt;
       '-------- Da kommt dann Code rein&lt;br /&gt;
       STS   {Meinfeld}, R24  &lt;br /&gt;
       $end Asm&lt;br /&gt;
       PRINT  str(Meinfeld)      '&amp;quot;AUSGABE&amp;quot;&lt;br /&gt;
   END IF&lt;br /&gt;
&lt;br /&gt;
   LOOP                          'Wiederholung&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, wie das Beispiel jetzt ist, ändern wir aber nichts wirklich, sondern schauen mal, was passiert.&lt;br /&gt;
&lt;br /&gt;
Und es passiert Merkwürdiges:&lt;br /&gt;
&lt;br /&gt;
Wenn wir das laufen lassen und in vielleicht gewohnter Manier &amp;quot;85&amp;quot; tippen und &amp;lt;ENTER&amp;gt; drücken, erscheint auf dem Terminal &lt;br /&gt;
 56&lt;br /&gt;
 53&lt;br /&gt;
 13&lt;br /&gt;
Einerseits ist es ja klar, wenn wir drei Tasten drücken, kriegen wir auch dreimal was zurück, aber wie bekommen wir dann in EIN Byte EINE Zahl &amp;quot;85&amp;quot; ? &lt;br /&gt;
&lt;br /&gt;
Also ein kurzer Side-Step.&lt;br /&gt;
&lt;br /&gt;
==Das Byte und seine vielen Bedeutungen==&lt;br /&gt;
&lt;br /&gt;
Nach wie vor ist klar: Ein Byte besteht aus 8 Bit, die in 256 Möglichkeiten kombiniert werden können. &lt;br /&gt;
&lt;br /&gt;
Das sagt aber nicht aus, was irgendeine dieser Bit-Kombinationen eigentlich '''bedeutet'''.&lt;br /&gt;
&lt;br /&gt;
*Das Byte als Bit-Container&lt;br /&gt;
Da hat jedes Bit seine eigene Bedeutung, unabhängig von den anderen. Also einfach &amp;quot;Schalter&amp;quot; und/oder JA-Nein Anzeigen. &lt;br /&gt;
&lt;br /&gt;
Typisch: '''Status-Register (SREG)'''.&lt;br /&gt;
&lt;br /&gt;
*Das Byte als (binär) Zahl. Hier repräsentieren die 256 Möglichkeiten wirklich die Zahlen von 0-255. Mit diesen Zahlen kann nach den binären Regeln gerechnet werden. &lt;br /&gt;
*Das Byte als zwei '''BCD'''Zahlen. Da besteht das Byte eigentlich aus zweimal 4 Bit (&amp;quot;Nibbles&amp;quot;). Jedes Nibble stellt die Zahlen 0 - 9 dar. Ist inzwischen eine recht exotische Verwendung, überhaupt als &amp;quot;gepacktes&amp;quot; Format, wo in einem Nibble auch noch ein Vorzeichen reinkodiert wurde. &lt;br /&gt;
*Das Byte als (binär) Zahl mit Vorzeichen. Das ist eine Kombination von Zahl und Schalter. Das Bit 2^^7 zeigt das Vorzeichen an. Solange es NULL ist, können die restlichen 7 Bit für Zahlen von (+) 0 - 127 normal verwendet werden. Ist das Vorzeichenbit = &amp;quot;1&amp;quot;, gelten die anderen Bit als Negativ-Zahl. Und sie werden als 2- Komplement dargestellt, d.h. 11111111 = -1. Dadurch geht das Bereich von -128 (10000000) bis +127 (01111111).&lt;br /&gt;
*Das Byte als Zahl, die Zahl ist aber ein Tabellenwert in einer standardisierten Tabelle.  &lt;br /&gt;
Typisch: '''ASCII und seine Varianten'''. Was heißt das ? Nun, gespeichert und hin-und hergeschickt wird zum Beispiel 01000001, das bedeutet aber nicht &amp;amp;H41 oder dezimal 65, sondern das, was in der ASCII-Tabelle an der Stelle 65 steht, und das ist ein großes &amp;quot;A&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Ich werd' mich hüten, hier eine ASCII-Tablle reinzustellen, die gibt es im Internet in Massen. Festgelegt sind die Werte 0 - 127. Wichtig ist nur die Gruppierung:&lt;br /&gt;
*0-31 sind &amp;quot;Steuerzeichen&amp;quot; für die Kommunikation. (&amp;quot;13&amp;quot; stellt das berühmte &amp;lt;ENTER&amp;gt; dar)&lt;br /&gt;
*32 bezeichnet eine Leerstelle (&amp;quot;blank&amp;quot;)&lt;br /&gt;
*48 - 57  für die Ziffern 0 - 9&lt;br /&gt;
*65 - 90  Großbuchstaben &amp;quot;A&amp;quot; bis &amp;quot;Z&amp;quot;&lt;br /&gt;
*97 - 122 Kleinbuchstaben &amp;quot;a&amp;quot; bis &amp;quot;z&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Numerischer Input vom Terminal==&lt;br /&gt;
Nachdem sich offenbar unterscheidet, was wir direkt vom Terminal bekommen und was wir davon brauchen können, brauchen wir ein zweites Datenfeld. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000               ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                     ' die Baudrate für das Terminal&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte             ' das bekommen wir vom Terminal&lt;br /&gt;
Dim Meinezahl As Byte            ' das soll die eigentliche Zahl werden&lt;br /&gt;
&lt;br /&gt;
DO&lt;br /&gt;
   Meinfeld = INKEY()            '&amp;quot;EINGABE&amp;quot;&lt;br /&gt;
   IF Meinfeld &amp;lt;&amp;gt; 0 THEN         '&amp;quot;BEDINGUNG&amp;quot;&lt;br /&gt;
       $asm                      '&amp;quot;BEARBEITUNG&amp;quot;&lt;br /&gt;
       '....   CODE .... (s.u.)&lt;br /&gt;
       $end Asm&lt;br /&gt;
       PRINT  str(Meinezahl)     'die zeigt er nach jeder Taste an&lt;br /&gt;
   END IF&lt;br /&gt;
   LOOP                          'Wiederholung&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Soweit das Gerüst bzw. der Rahmen. In &amp;quot;Meinfeld&amp;quot; stellt der Bascom rein, was getippt wurde und wir müssen das verarbeiten und stellen das jeweilige Ergebnis nach &amp;quot;Meinezahl&amp;quot; rein. &lt;br /&gt;
&lt;br /&gt;
Was ist zu tun ? &lt;br /&gt;
*Prüfen, was getippt wurde.&lt;br /&gt;
*davon abhängig &lt;br /&gt;
**Meinfeld=13 (&amp;lt;ENTER&amp;gt;). Die bisher bekommene Zahl ist komplett, jetzt könnten wir mit ihr irgendetwas rechnen oder sonstwas tun. &lt;br /&gt;
**Meinfeld= 48-57 ('0'-'9'). &lt;br /&gt;
***Das, was bisher in &amp;quot;Meinezahl&amp;quot; steht, multiplizieren wir mit 10, an der Einerstelle steht nun auf jeden Fall einen NULL, die bisherigen Zahlen links davon.&lt;br /&gt;
***Und dann müssen wir das, was reingekommen ist, auf binär 0-9 umwandeln und draufaddieren. &lt;br /&gt;
**Alles Andere ignorieren wir einfach.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000                                          ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                                                ' die Baudrate für das Terminal&lt;br /&gt;
&lt;br /&gt;
Dim Meinfeld As Byte&lt;br /&gt;
Dim Meinezahl As Byte&lt;br /&gt;
Do&lt;br /&gt;
   Meinfeld = Inkey()                                       '&amp;quot;EINGABE&amp;quot;&lt;br /&gt;
   If Meinfeld &amp;lt;&amp;gt; 0 Then                                    '&amp;quot;BEDINGUNG&amp;quot;&lt;br /&gt;
       $asm                                                 '&amp;quot;BEARBEITUNG&amp;quot;&lt;br /&gt;
       lds     r22, {Meinezahl}                             'die bisherige Zahl&lt;br /&gt;
       lds     r24, {Meinfeld}                              'die neue Ziffer&lt;br /&gt;
       cpi     r24, 13                                      '&amp;lt;ENTER&amp;gt; ?&lt;br /&gt;
       brne    Is_numerisch&lt;br /&gt;
       ' ENTER Gedrückt&lt;br /&gt;
       ' Zahl verarbeiten&lt;br /&gt;
       ' dann löschen für ein Neues&lt;br /&gt;
       clr   r22&lt;br /&gt;
       rjmp    Fertig&lt;br /&gt;
Is_numerisch:&lt;br /&gt;
       cpi     r24, 48                                      '48 = '0'&lt;br /&gt;
       brlo    Fertig                                       'keine Ziffer--&amp;gt;ignorieren&lt;br /&gt;
       cpi     r24, 57 + 1                                  '57 = '9'&lt;br /&gt;
       brsh    Fertig                                       'keine Ziffer--&amp;gt;ignorieren&lt;br /&gt;
Einfügen:&lt;br /&gt;
' Bisherige Zahl * 10   Methode:  n * 10 =&amp;gt; n (8 + 2) =&amp;gt; n * 8 + n * 2&lt;br /&gt;
       lsl     r22                                          ' (n * 2)&lt;br /&gt;
       mov     r23, r22                                     ' kopieren&lt;br /&gt;
       lsl     r23                                          ' (n * 2) * 2&lt;br /&gt;
       lsl     r23                                          ' (n * 2 * 2) * 2&lt;br /&gt;
       add     r22, r23                                     'n * 8 + n * 2&lt;br /&gt;
' neue Ziffer umwandeln&lt;br /&gt;
       andi    r24, 15&lt;br /&gt;
' und addieren&lt;br /&gt;
       add     r22, r24&lt;br /&gt;
Fertig:&lt;br /&gt;
       STS   {Meinezahl}, R22&lt;br /&gt;
       $end Asm&lt;br /&gt;
       Print Str(meinezahl)                                 '&amp;quot;AUSGABE&amp;quot;&lt;br /&gt;
   End If&lt;br /&gt;
   Loop                                                     'Wiederholung&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Da sind ein paar Sachen dabei, die hatten wir noch nicht&lt;br /&gt;
&lt;br /&gt;
*Ich beginne mal von hinten: Bei &amp;quot;Fertig&amp;quot; wird das Register R22 nach &amp;quot;Meinezahl&amp;quot; geschrieben, Bascom zeigt es dann her. Wir müssen also sehen, das in R22 immer das Richtige drin steht.&lt;br /&gt;
*Also, jetzt wieder von vorn, wir holen den bisherigen Wert von &amp;quot;Meinezahl&amp;quot; gleich mal ins R22.&lt;br /&gt;
(Das ist am Anfang einfach NULL)&lt;br /&gt;
*Dann die neue Eingabe in R24&lt;br /&gt;
*Die vergleichen wir mir &amp;quot;13&amp;quot; == &amp;lt;ENTER&amp;gt;&lt;br /&gt;
**ist es eine andere Eingabe (BRNE), schauen wir bei &amp;quot;Is_numerisch&amp;quot; weiter&lt;br /&gt;
**Wenn aber ja, könnten wir jetzt was tun. Wir machen aber weiter nichts, löschen aber R22 und damit &amp;quot;Meinezahl&amp;quot; (s.o) auf NULL, damit wir für eine neue Eingabe bereit sind.&lt;br /&gt;
*Is_numerisch: &lt;br /&gt;
**Die Eingabe ist kleiner als 48 ('0'), keine Ziffer, wir sind auch schon fertig&lt;br /&gt;
**Jetzt müßten wir vergleichen, ob die Eingabe größer als 57 ('9') ist. Das geht nicht [[Assembler_Einf%C3%BChrung_f%C3%BCr_Bascom-User#Verzweigen|(siehe weiter oben bei den Verzweigungen)]], also vergleichen wir mit 57+1, bei gleich oder größer ist die Eingabe auch keine Ziffer, also --&amp;gt; fertig.&lt;br /&gt;
*Einfügen: &lt;br /&gt;
**Bisheriger Wert * 10: Bei manchen µC gibt es einen Multiplikationsbefehl, aber diese einfache Rechnung machen wir zu Fuß, ist einfach mehr Spaß und mehr neue Befehle. &lt;br /&gt;
&lt;br /&gt;
n * 10 kann man ja zerlegen in n*(8+2). Und sowohl 8 als auch 2 sind ja 2-er Potenzen, da kann einfach Bit-weise nach links schieben.&lt;br /&gt;
 &amp;quot;LSL  R22&amp;quot;  ist gleich  R22 * 2   &amp;quot;LSL  Logical Shift Left&amp;quot;&lt;br /&gt;
damit haben wir schon mal Meinezahl * 2 gerechnet. Das kopieren wir nach R23 und schieben dort noch zweimal nach links. In R23 steht nun also insgesamt Meinezahl * 8. Das addieren wir nun.  &lt;br /&gt;
*Umwandeln: Die Ziffern '0'-'9', also 48 - 57 schauen hexadezimal ja so aus:&lt;br /&gt;
 0x30 = dezimal 48 = ASCII '0' bis &lt;br /&gt;
 0x39 = dezimal 57 = ASCII '9'&lt;br /&gt;
Wir brauchen also nur die '3' irgendwie zu löschen, dann haben wir sofort unsere 0-9. Das geschieht durch &lt;br /&gt;
 ANDI     R24, 15          ' &amp;quot;AND&amp;quot; mit einer Konstanten &lt;br /&gt;
In R24 bleiben nur die 1-er übrig, wo auch in '15' ein 1-er ist. Damit ist die '3' weg.&lt;br /&gt;
*Addieren&lt;br /&gt;
 ADD  R22, r24           'Meinezahl + neuerWert&lt;br /&gt;
und fertig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine Anmerkung: Wir haben noch keine Prüfung, ob die Zahl für ein Byte nicht zu groß wird. Wenn wir also 9999 eintippen, kommt Schrott heraus.&lt;br /&gt;
&lt;br /&gt;
==Mehr als ein Byte==&lt;br /&gt;
Mit einzelnen Bytes geht es ja jetzt schon recht gut. Aber es gibt ja auch größere Zahlen. &lt;br /&gt;
===16 Bit Zahlen===&lt;br /&gt;
Das nächstgrößere ist eine 16-Bit Zahl. Bei Bascom ist das dann ein &lt;br /&gt;
*WORD   für den Zahlenbereich 0 - 65535 und &lt;br /&gt;
*INTEGER für -32768 bis +32767&lt;br /&gt;
&lt;br /&gt;
Beides wird in zwei hintereinanderliegenden Bytes gespeichert, also  &lt;br /&gt;
 MeinByte und &lt;br /&gt;
 MeinByte + 1&lt;br /&gt;
Und zwar so, daß erst das niederwertigere und dann das höherwertige Byte gespeichert werden. &lt;br /&gt;
Also die 16-Bit Binärzahl  &lt;br /&gt;
 0001001000110100 = Hexadezimal &amp;amp;H1234 &lt;br /&gt;
steht im Speicher in zwei Bytes als&lt;br /&gt;
 00110100 00010010 =  &amp;amp;H34 &amp;amp;H12&lt;br /&gt;
&lt;br /&gt;
Genauso ist es mit den Registern, üblicherweise steht das erste Byte in einem geradzahligen Register und das zweite im nächsthöherem. z.B.:&lt;br /&gt;
 R24: 00110100 =  &amp;amp;H34&lt;br /&gt;
 R25: 00010010 =  &amp;amp;H12&lt;br /&gt;
Sowas nennt man dann &amp;quot;Register-Paar&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Der AVR µC kann einige seiner 32 Register als Registerpaar verwenden. Das sind &lt;br /&gt;
 R0:R1       (aber nur bei den Multiplikationsbefehlen für das Ergebnis)&lt;br /&gt;
 R24:R25     (für 16-Bit Berechnungen)&lt;br /&gt;
 R26:R27     (für 16-Bit Berechnungen und als &amp;quot;Pointer&amp;quot; X)&lt;br /&gt;
 R28:R29     (für 16-Bit Berechnungen und als &amp;quot;Pointer&amp;quot; Y)&lt;br /&gt;
 R30:R31     (für 16-Bit Berechnungen und als &amp;quot;Pointer&amp;quot; Z)&lt;br /&gt;
Wobei es aber nur zwei solche Berechnungen gibt&lt;br /&gt;
 ADIW     R24 , zahl       ' addieren zahl auf r24:r25&lt;br /&gt;
 SBIW     R24 , zahl       ' subtrahieren zahl von r24:r25&lt;br /&gt;
Die anderen 16-Bit Befehle sind eher Adress-Arithmetik. &lt;br /&gt;
&lt;br /&gt;
*Datenaustausch Bascom &amp;lt;=&amp;gt; Assembler&lt;br /&gt;
Um ein WORD (oder INTEGER) mit Bascom auszutauschen, geht z.B. folgendes&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 DIM word_1 AS WORD&lt;br /&gt;
     $asm&lt;br /&gt;
     LDS      R24, {word_1}    'holen Byte 1  (bits 0-7)&lt;br /&gt;
     LDS      R25, {word_1+1}  'holen Byte 2  (bits 8-15)&lt;br /&gt;
     ADIW     R24, 42          'addieren von 42 auf das Paar r24:r25&lt;br /&gt;
     STS      {word_1}, R24    'speichern Byte 1&lt;br /&gt;
     STS      {word_1+1}, R25  'speichern Byte 2&lt;br /&gt;
     $end asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Arithmetik mit 16 Bit &lt;br /&gt;
Der Unterschied zur 8-Bit Verarbeitung ist eigentlich nicht groß. Es gibt zu allen Arithmetik Befehlen des AVR eine &amp;quot;Carry&amp;quot;-Variante, die einen Übertrag von der Aktion vorher berücksichtigt. &lt;br /&gt;
&lt;br /&gt;
Angenommen zwei 16-Bit Werte in den Registerpaaren R24:R25 und R22:R23&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ADD    R24, R22           'Addieren der niederwertigen Bytes&lt;br /&gt;
 ADC    R25, R23           'Addieren der höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
&lt;br /&gt;
 SUB    R24, R22           'Subtrahieren der niederwertigen Bytes&lt;br /&gt;
 SBC    R25, R23           'Subtrahieren der höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Vergleichen mit 16 Bit &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 CP     R24, R22           'Vergleich der niederwertigen Bytes&lt;br /&gt;
 CPC    R25, R23           'Vergleich der höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
           ' jetzt können die normalen &amp;quot;bedingte Verzweigung&amp;quot;-Befehle verwendet werden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===32 Bit Zahlen===&lt;br /&gt;
Bei Bascom heissen die LONG und die sind immer Vorzeichenbehaftet. Im AVR Instructionset gibt es keine 32-Bit Befehle. Die physische Speicherung ist wie bei den 16-Bit Feldern&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 DIM long_1 AS LONG&lt;br /&gt;
     $asm&lt;br /&gt;
     LDS      R24, {long_1}    'holen Byte 1  (bits 0-7)&lt;br /&gt;
     LDS      R25, {long_1+1}  'holen Byte 2  (bits 8-15)&lt;br /&gt;
     LDS      R26, {long_1+2}  'holen Byte 3  (bits 16-23)&lt;br /&gt;
     LDS      R27, {long_1+3}  'holen Byte 4  (bits 24-31)&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Arithmetik mit 32 Bit &lt;br /&gt;
Eigentlich läuft das ab 16-Bit immer gleich ab, und man kann genauso 24-Bit wie 40 oder 48 Bit rechnen. &lt;br /&gt;
Angenommen zwei 32-Bit Werte in den Registern  R16-&amp;gt;R19 und R20-&amp;gt;R23&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ADD    R16, R20           'Addieren der niederwertigen Bytes&lt;br /&gt;
 ADC    R17, R21           'Addieren des nächst-höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
 ADC    R18, R22           'Addieren des nächst-höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
 ADC    R19, R23           'Addieren des nächst-höherwertigen Bytes + ev. Überlauf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Single und Double Zahlen===&lt;br /&gt;
das sind 32- bzw. 64-Bitzahlen, allerdings haben die eine eigene Float-Struktur. Das ist dann schon ein eigenes Thema, mit denen was zu machen. &lt;br /&gt;
&lt;br /&gt;
===Strings===&lt;br /&gt;
Strings sind Bytefolgen, in denen ASCII-Zeichen einfach von links nach rechts gespeichert werden können. Das Ende-Kennzeichen der immer variabel langen Texte ist ein NULL-Byte. Daher braucht in Bascom ein String für (max) 20 Zeichen lange Strings auch in Wirklichkeit 21 Byte Speicher.&lt;br /&gt;
&lt;br /&gt;
Strings sind gut geeignet, eine andere Art vorzustellen, wie man Daten lesen oder speichern kann: Über POINTER-Register&lt;br /&gt;
&lt;br /&gt;
Gleich ganz in die Praxis: Wir wollen die Länge eines Bascom-Strings festellen, natürlich im Assembler&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000                                          ' der Quartz, den wir verwenden&lt;br /&gt;
$baud = 9600                                                ' die Baudrate für das Terminal&lt;br /&gt;
&lt;br /&gt;
Dim Text As String * 20                                     ' das ist der String&lt;br /&gt;
Dim Strlen As Byte                                          ' da sol die Länge rein&lt;br /&gt;
&lt;br /&gt;
      Text = &amp;quot;Hello, world !&amp;quot;                               ' wir befüllen den String&lt;br /&gt;
&lt;br /&gt;
      $asm&lt;br /&gt;
      Loadadr Text , X            '&amp;quot;adresse von Text nach R26:r27&amp;quot; &lt;br /&gt;
      clr      r24                'löschen vom Zähler&lt;br /&gt;
Schleife: &lt;br /&gt;
      ld       r22, X+            ' s.u.&lt;br /&gt;
      cpi      r22, 0             'Vergleichen mit NULL&lt;br /&gt;
      breq     Fertig             'fertig&lt;br /&gt;
      inc      r24                'Zähler erhöhen&lt;br /&gt;
      rjmp     Schleife           'weiter&lt;br /&gt;
Fertig:&lt;br /&gt;
      sts   {strlen}, r24         'ergebnis ablegen&lt;br /&gt;
      $end Asm&lt;br /&gt;
&lt;br /&gt;
      Print Str(strlen)           'herzeigen&lt;br /&gt;
&lt;br /&gt;
End&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*'''Loadadr''' ist ein Bascom-Statement, mit dem das Registerpaar R26:R27 mit der '''Adresse''' von &amp;quot;Text&amp;quot; geladen wird. In einem &amp;quot;echten&amp;quot; Assembler kann man das auch assemblermäßiger formulieren, aber in Bascom muß man das so machen&lt;br /&gt;
*'''LD'''&lt;br /&gt;
      ld       r22, X+        'Das Zeichen, wo X hinzeigt, nach R22 und X um eins erhöhen&lt;br /&gt;
Den Rest kennen wir eigentlich schon.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''ST''' ist übrigens das Gegenstück zu LD&lt;br /&gt;
      st       X+, r22        'R22 dorhin, wo X hinzeigt, und dann X um eins erhöhen&lt;br /&gt;
&lt;br /&gt;
==Bascom als Messlatte==&lt;br /&gt;
Eine sehr gute Möglichkeite, Assembler zu trainieren, ist es, irgendwelche Bascom-Statements durch eigene Inline-Assembler-Blöcke zu ersetzen. Dabei kann man immer auch Varianten ausprobieren. &lt;br /&gt;
*Beispiel&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DIM  Wert1 AS WORD&lt;br /&gt;
DIM  Wert2 AS WORD&lt;br /&gt;
        Wert1 = Wert2   ' Bascom-Statement&lt;br /&gt;
&lt;br /&gt;
'als Inline-Assembler  EINE Möglichkeit&lt;br /&gt;
        $asm&lt;br /&gt;
        LDS     R24, {Wert1}&lt;br /&gt;
        STS     {Wert2}, R24&lt;br /&gt;
        LDS     R24, {Wert1+1}&lt;br /&gt;
        STS     {Wert2+1}, R24&lt;br /&gt;
        $end asm&lt;br /&gt;
'als Inline-Assembler  ANDERE Möglichkeit&lt;br /&gt;
        $asm&lt;br /&gt;
        LOADADR Wert1, X&lt;br /&gt;
        LD     R24, X+&lt;br /&gt;
        LD     R25, X+&lt;br /&gt;
        LOADADR Wert2, X&lt;br /&gt;
        ST     X+, R24&lt;br /&gt;
        ST     X+, R25&lt;br /&gt;
        $end asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bascom_Inside-Code|Es gibt hier ein paar Beispiele]], wie Bascom dieses oder jenes in Maschinencode umsetzt, dabei ist es auch sehr lehrreich, erstmal zu überlegen, wie man es selbst machen würde, und dann erst nachzusehen, wie es Bacom gelöst hat.&lt;br /&gt;
&lt;br /&gt;
==ALU und Status-Register (SREG)==&lt;br /&gt;
Bisher haben wir nur von Zero-Bits und &amp;quot;Oberflow&amp;quot; bzw. Carry-Bit gesprochen und uns da ein wenig drübergeschwindelt. Es ist ja auch tatsächlich so, daß man mit dem bisher erworbenen Wissen ganze Legionen von Robotern programmieren kann. &lt;br /&gt;
&lt;br /&gt;
Aber ganz kommt man an den diversen Flags im Statusregister ja doch nicht vorbei, schon garnicht, wenn man ernsthafte Berechnungen anstellen will.  &lt;br /&gt;
&lt;br /&gt;
Zwei Bit im SREG brauchen wir an dieser Stelle aber nicht weiter zu beachten&lt;br /&gt;
*T-Bit oder Transfer-Bit. Das dient für einige Befehle als &amp;quot;Zwischendepot&amp;quot; für einzelne Bits&lt;br /&gt;
*Global Interrupt Enable-Bit. Wie der Name sagt, werden damit einfach alle Interrupts zugelassen oder eben nicht. Von Interrupts haben wir aber noch garnicht gesprochen &lt;br /&gt;
*Die andern Bits dienen der ALU (Arithmetic-Logical-Unit) als Ergebnis-Anzeige bei den ganzen arithmetisch-logischen Befehlen&lt;br /&gt;
===ALU (Recheneinheit)=== &lt;br /&gt;
Immer, wenn zwei Bytes zu vermangeln sind, geht das mit der Recheneinheit. Die hat 4 Byte zur Verfügung:&lt;br /&gt;
*Input-Byte A&lt;br /&gt;
*Input-Byte B&lt;br /&gt;
*Output-Byte R&lt;br /&gt;
*Statusregister SREG&lt;br /&gt;
(Daß er dabei Input A gleichzeitig als Output verwendet, ignorieren wir mal einfach, das verwirrt nur und ändert nix grundsätzliches)&lt;br /&gt;
&lt;br /&gt;
====Ein-Bit-Volladdierer==== &lt;br /&gt;
Wir wissen ja alle noch, wie das mit dem Addieren im 2-er System funktioniert ?  &lt;br /&gt;
 A   B    R&lt;br /&gt;
 0 + 0  = 0&lt;br /&gt;
 0 + 1  = 1&lt;br /&gt;
 1 + 0  = 1&lt;br /&gt;
 1 + 1  = 0  und Überlauf&lt;br /&gt;
Diesen Überlauf muß man nun an das nächsthöhere Bit weitergeben. Andererseits kommt von dem niedrigeren Bit ja auch ein Überlauf daher. Also sieht das für ein Bit dann so aus&lt;br /&gt;
[[Bild:ALU1.png]]&lt;br /&gt;
&lt;br /&gt;
====Mit 8-Bit==== &lt;br /&gt;
Für alle 8 Bit wird es etwas größer, ein paar direkte Status-Bit hab ich gleich eingezeichnet&lt;br /&gt;
[[Bild:ALU2.png]]&lt;br /&gt;
*Der Schalter &amp;quot;mit od. ohne Carry&amp;quot; ist da, ob man einen vorherigen Überlauf miteinbeziehen will &lt;br /&gt;
 ADD   register, register         OHNE Carry &lt;br /&gt;
 ADC   register, register         MIT  Carry&lt;br /&gt;
*N-Bit:  Ist einfach der Wert (0 oder 1), den das Bit 2^^7 im Ergebnis hat&lt;br /&gt;
*Z-Bit:  Steht im Ergebnis alles auf 0, wird das gesetzt&lt;br /&gt;
*Half-Carry: Das wird gesetzt, wenn es vom Bit 2^^3 einen Überlauf in Richtung 2^^4 gegeben hat. (Das braucht man, wenn man mit BCD-Zahlen, also nur von 0-9 rechnen will. Dazu später, wenn Interesse da ist)&lt;br /&gt;
*Carry-Bit: Ist nun klar, das ist einfach der Überlauf vom 2^^7 Bit.&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;S&amp;quot; und &amp;quot;V&amp;quot; Bit====&lt;br /&gt;
=====8 Bit mit Vorzeichen=====&lt;br /&gt;
Kurze Wiederholung *gähn*, wie das mit den Vorzeichen ist: Man &amp;quot;spiegelt&amp;quot; die Zahlen an der Null-Stelle. Nehmen wir an, wir haben grad eine NULL im Byte. Ziehen wir jetzt 1 ab, soll ja offenbar -1 rauskommen. Was steht im Byte tatsächlich drin ? Richtig, Hexadezimal FF. Und genauso schaut auch -1 eben aus. Alle diese negativen Zahlen haben gemeinsam, daß zumindest das Bit 2^^7 eine 1 zeigt. Daran erkennt man, daß die Zahl negativ ist. Das funktioniert aber nur so bis -128 (hex 80) und +127 (hex 7F). &lt;br /&gt;
&lt;br /&gt;
Negative Zahlen werden also dargestellt als &amp;quot;2-er Komplement&amp;quot;. &lt;br /&gt;
*Man dreht 0-er und 1-er im Byte um&lt;br /&gt;
*und addiert &amp;quot;1&amp;quot; drauf. &lt;br /&gt;
           0b01111000 hex 78 = dezimal 120&lt;br /&gt;
 umdrehen: 0b10000111 hex 87  &lt;br /&gt;
 +1      : 0b10001000 hex 88  Das stellt jetzt -120 dar.&lt;br /&gt;
&lt;br /&gt;
=====Rechnen mit vorzeichenbehafteten 8 Bit=====&lt;br /&gt;
Der Sinn und Zweck des &amp;quot;S&amp;quot; und &amp;quot;V&amp;quot; Bit ist nun leicht verständlich. &lt;br /&gt;
*Bisher wurde ja addiert&lt;br /&gt;
     0b01111000 hex 78 = dezimal 120&lt;br /&gt;
   + 0b00001010 hex 0A = dezimal  10&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82 = dezimal 130       &lt;br /&gt;
*Addiert wird jetzt immer noch genauso, aber jetzt hat das Bit 2^^7 eine andere Bedeutung, weil es das Vorzeichen darstellt&lt;br /&gt;
*Ein Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0b10000000 hex 80 = dezimal -128&lt;br /&gt;
   + 0b00000010 hex 02 = dezimal   +2&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82  (Vorzeichenbit ist gesetzt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vorzeichenbit gesetzt-&amp;gt; hex 82 ist also ein zweier-Komplement, und damit ist es -126.&lt;br /&gt;
&lt;br /&gt;
*Aber nun:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0b01111000 hex 78 = dezimal +120&lt;br /&gt;
   + 0b00001010 hex 0A = dezimal  +10&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82  (Vorzeichenbit ist gesetzt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vorzeichenbit gesetzt-&amp;gt; hex 82 ist also ein zweier-Komplement, und damit ist es -126.&lt;br /&gt;
&lt;br /&gt;
'''Bitte ???'''&lt;br /&gt;
&lt;br /&gt;
Das kann ja wohl nicht sein, es muß ja +130 rauskommen. &lt;br /&gt;
&lt;br /&gt;
Das Carry-Bit hilft nichts, das ist immer NULL geblieben. &lt;br /&gt;
&lt;br /&gt;
*Genau da hilft das &amp;quot;V&amp;quot; Bit&lt;br /&gt;
&lt;br /&gt;
[[Bild:ALU3.png]]&lt;br /&gt;
&lt;br /&gt;
Das V-Bit = 1, &lt;br /&gt;
*wenn entweder die beiden Input-Bytes positiv waren (2^^7=0) UND das Ergebnis aber 2^^7=1 zeigt. &lt;br /&gt;
*wenn beiden Input-Bytes negativ waren (2^^7=1) UND das Ergebnis aber 2^^7=0 zeigt. &lt;br /&gt;
&lt;br /&gt;
Im Prinzip sagt also das V-Bit aus, ob das Vorzeichenbit im Ergebnis durch einen Überlauf zustandegekommen ist. &lt;br /&gt;
&lt;br /&gt;
*Und das &amp;quot;S&amp;quot;-Bit ? Das kombiniert das Vorzeichen im Ergebnis mit diesem &amp;quot;V&amp;quot;-Bit.&lt;br /&gt;
**S=1, wenn das Vorzeichenbit im Ergebnis zwar NULL ist, aber nur, weil ein Überlauf stattgefunden hat. Das Ergebnis ist also trotzdem negativ und stellt ein 2-er Komplement dar.  &lt;br /&gt;
**S=1, wenn das Vorzeichenbit im Ergebnis EINS ist, und zwar OHNE, daß ein Überlauf stattgefunden hätte. Das Ergebnis ist also korrekt negativ und stellt auch ein 2-er Komplement dar&lt;br /&gt;
&lt;br /&gt;
Ob an das nächsthöhere Byte ein Überlauf weiterzugeben ist, sagt bei Vorzeichen-Bytes nun das &amp;quot;V&amp;quot; Bit.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nochmal die obigen Vorzeichen-Beispiele zum mitsingen, aber diesmal mit den N, V und S Flags &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0b10000000 hex 80 = dezimal -128&lt;br /&gt;
   + 0b00000010 hex 02 = dezimal   +2&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82   N=1 V=0 S=1    Ist also negativ, 2-er Kompl. kein Überlauf --&amp;gt;  -126&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
     0b01111000 hex 78 = dezimal +120&lt;br /&gt;
   + 0b00001010 hex 0A = dezimal  +10&lt;br /&gt;
     -------------------------------&lt;br /&gt;
     0b10000010 hex 82   N=1 V=1 S=0    Ist also positiv, 2^^7 löschen, der Rest bleibt &lt;br /&gt;
                                        (dezimal 2), hat aber Überlauf, der ist +128 wert, &lt;br /&gt;
                                        insgesamt also --&amp;gt; 128 + 2 = +130&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Mehr als 8 Bit mit Vorzeichen=====&lt;br /&gt;
Keine Sorge, das Vorzeichenbit gibt es bei allen binärzahlen immer nur einmal, an der höchsten Stelle. Wir haben also auch bei eine 8-Byte / 64-Bit Variablen nur ein einziges Mal das Problem.&lt;br /&gt;
&lt;br /&gt;
*Das Vorzeichenbit ist immer das MSB (höchst aussagekräftige Bit = most significant Bit). Zwei 32 Bit sieht das so aus:&lt;br /&gt;
 0b00000000000000000000000111111111 = hex 000001FF = dezimal +511&lt;br /&gt;
 0b11111111111111111111110000000001 = hex FFFFFC01 = dezimal -1023&lt;br /&gt;
Das Vorzeichenbit ist das ganz links (2^^31) &lt;br /&gt;
&lt;br /&gt;
Addiert werden solche Zahlen erstmal ganz normal &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dim Vala As Long&lt;br /&gt;
Dim Valb As Long&lt;br /&gt;
Dim Stat As Byte&lt;br /&gt;
&lt;br /&gt;
      Vala = 511&lt;br /&gt;
      Valb = -1023&lt;br /&gt;
      Print Hex(vala) ; &amp;quot;+&amp;quot; ; Hex(valb) ; &amp;quot;=&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
      $asm&lt;br /&gt;
      lds      r16, {vala}&lt;br /&gt;
      lds      r17, {vala+1}&lt;br /&gt;
      lds      r18, {vala+2}&lt;br /&gt;
      lds      r19, {vala+3}&lt;br /&gt;
&lt;br /&gt;
      lds      r20, {valb}&lt;br /&gt;
      lds      r21, {valb+1}&lt;br /&gt;
      lds      r22, {valb+2}&lt;br /&gt;
      lds      r23, {valb+3}&lt;br /&gt;
&lt;br /&gt;
      add      r16, r20            'das erste Byte noch ohne Carry&lt;br /&gt;
      adc      r17, r21            'der Rest mit Carry&lt;br /&gt;
      adc      r18, r22&lt;br /&gt;
      adc      r19, r23&lt;br /&gt;
&lt;br /&gt;
      in       r20, sreg           ' Wir holen uns die Status-Bits&lt;br /&gt;
      sts      {stat}, r20         ' speichern&lt;br /&gt;
&lt;br /&gt;
      sts      {Vala}, r16         ' speichern Ergebnis&lt;br /&gt;
      sts      {Vala+1}, r17&lt;br /&gt;
      sts      {Vala+2}, r18&lt;br /&gt;
      sts      {Vala+3}, r19&lt;br /&gt;
&lt;br /&gt;
      $end Asm&lt;br /&gt;
      Print Hex(vala) ; &amp;quot; &amp;quot; ; Bin(stat) ; &amp;quot; &amp;quot; ; Str(vala)            ' Zum Anschauen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Wir sehen, gesetzt wird von der ALU letzlich: &lt;br /&gt;
 S=1 und N=1&lt;br /&gt;
Also ist die Zahl negativ (S=1) und muß als 2-er Komplement verstanden werden.&lt;br /&gt;
&lt;br /&gt;
===Das Carry-Bit beim Bit-Verschieben===&lt;br /&gt;
Häufig wird das Carry-Bit auch als Zwischenlager und Anzeige bei den Schiebe- und Rotationsbefehlen. &lt;br /&gt;
&lt;br /&gt;
Es verhält sich da wie ein 9.Bit zu den 8-Bits im Register. &lt;br /&gt;
&lt;br /&gt;
Die Links- und Rechts-Schiebebefehle sind völlig symmetrisch&lt;br /&gt;
====LSL &amp;amp; ROL====&lt;br /&gt;
[[Bild:Left.png]]&lt;br /&gt;
 LSL register &lt;br /&gt;
 ROL register&lt;br /&gt;
Die Bits im Register werden bei beiden Befehlen eine Stelle nach links verschoben, das höchste Bit wandert in das Carry-Bit. &lt;br /&gt;
*Bei LSL wird eine NULL in das niederwertigste Bit des Registers reingeschoben&lt;br /&gt;
*Bei ROL wird das Carry-Bit (von vorher) in dieses Bit gestellt.&lt;br /&gt;
&lt;br /&gt;
====LSR &amp;amp; ROR====&lt;br /&gt;
[[Bild:Right.png]]&lt;br /&gt;
 LSR register &lt;br /&gt;
 ROR register&lt;br /&gt;
Die Bits im Register werden bei beiden Befehlen eine Stelle nach rechts verschoben, das niederste Bit wandert in das Carry-Bit. &lt;br /&gt;
*Bei LSR wird eine NULL in das höchste Bit des Registers reingeschoben&lt;br /&gt;
*Bei ROR wird das Carry-Bit (von vorher) in dieses Bit gestellt.&lt;br /&gt;
&lt;br /&gt;
====Anwendungen====&lt;br /&gt;
*Multiplizieren mit 2, 4, 8,... &lt;br /&gt;
Bei einem Byte ist da wohl nicht viel dazu zu sagen. &lt;br /&gt;
 LDI   register, &amp;amp;H05   ' register = &amp;amp;H05   = dezimal 5&lt;br /&gt;
 LSL   register         ' register = &amp;amp;H0A   = dezimal 10&lt;br /&gt;
Bei mehreren Bytes, also Variablen mit mehr als 8 Bit, beginnt man immer mit dem niederwertigsten Byte, der erste Befehl OHNE Carry, damit nicht irgendetwas ungewolltes reinrutscht, und dann weiter mit den anderen Bytes&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  LDS  register, {long_variable }    ' einlesen&lt;br /&gt;
  LSL  register                      ' verschieben, 2^^7 kommt ins Carry, bleibt&lt;br /&gt;
                                     ' 0 kommt nach 2^^0&lt;br /&gt;
  STS  {long_variable }, register    ' speichern (ändert nix im SREG)&lt;br /&gt;
&lt;br /&gt;
  LDS  register, {long_variable +1 }    ' einlesen (ändert auch nix im SREG)&lt;br /&gt;
  ROL  register                         ' verschieben, &lt;br /&gt;
                                        ' carry von vorher nach 2^^0&lt;br /&gt;
                                        ' 2^^7 ins Carry, bleibt&lt;br /&gt;
  STS  {long_variable + 1}, register    ' speichern &lt;br /&gt;
    .usw. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Dividieren durch 2, 4, 8,... &lt;br /&gt;
Da ist eben alles genau umgekehrt. Beginnen mit dem höchsten, sonst wie gehabt &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  LDS  register, {long_variable + 3}    &lt;br /&gt;
  LSR  register                         ' verschieben, 2^^0 kommt ins Carry und bleibt&lt;br /&gt;
                                        ' 0 kommt nach 2^^7&lt;br /&gt;
  STS  {long_variable +3}, register       &lt;br /&gt;
&lt;br /&gt;
  LDS  register, {long_variable + 2 }    &lt;br /&gt;
  ROR  register                         ' verschieben, &lt;br /&gt;
                                        ' carry von vorher nach 2^^7&lt;br /&gt;
                                        ' 2^^0 ins Carry&lt;br /&gt;
  STS  {long_variable + 2}, register    &lt;br /&gt;
    .usw. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Anwendung Lauflicht====&lt;br /&gt;
Ist auch beliebt. Angenommen, wir haben auf PORTB 8 LED hängen und möchten da ein Lauflicht haben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $asm&lt;br /&gt;
    LDI    r24, &amp;amp;HFF&lt;br /&gt;
    !OUT   DDRB, R24           ' wir setzen das Port auf OUTPUT&lt;br /&gt;
    LDI    r16, &amp;amp;H00           ' das &amp;quot;SchiebeByte&amp;quot; auf NULL&lt;br /&gt;
    SEC                        ' wir setzen das Carry Bit, damit ein 1-er da ist &lt;br /&gt;
Dauerschleife:&lt;br /&gt;
    ROL    r16                 ' Beim ersten Mal rutscht das carry-Bit rein&lt;br /&gt;
                               ' danach geht der 1-er automatisch im Kreis&lt;br /&gt;
    !OUT   PORTB, R16          ' wir legen das Byte mit dem einen 1-er an das Port&lt;br /&gt;
                               '---------------------------------------------------&lt;br /&gt;
                               ' Hier muß aber eine Verzögerung rein, sonst ist das &lt;br /&gt;
                               ' viel zu schnell&lt;br /&gt;
                               '---------------------------------------------------&lt;br /&gt;
    RJMP   Dauerschleife       ' und immer weiter&lt;br /&gt;
    $end asm&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Schreiben wir&lt;br /&gt;
     ROR    r16&lt;br /&gt;
geht's in die andere Richtung&lt;br /&gt;
&lt;br /&gt;
==Ecken, Kanten und Tücken==&lt;br /&gt;
Das AVR-Instruction-Set weist einige Feinheiten auf, die schon manchen Assembler-Roboter auf Kollisionskurs gebracht haben. &lt;br /&gt;
&lt;br /&gt;
Grad' dieses Kapitel kann ich nur nach und nach auffüllen, weil mir auch nicht immer gleich alles so einfällt. Vielleicht gibt's aber auch ein paar andere Assembleure, die ihren Erfahrungsschatz hier einbringen. &lt;br /&gt;
&lt;br /&gt;
Ich kann nur raten, beim Programmieren immer zumindest das &amp;quot;Complete Instruction Set Summary&amp;quot; bei der Hand zu haben. Immer wieder nachsehen, ob ein gerade ausgewählter Befehl das macht, was man sich vorstellt, und vor allem, ob er auch alle die Status-Register-Flags so setzt oder löscht, die man braucht. &lt;br /&gt;
&lt;br /&gt;
===INC/DEC===&lt;br /&gt;
Für Schleifenzähler etc. werden ja gerne &amp;quot;INC&amp;quot; (register +1) oder &amp;quot;DEC&amp;quot; (register -1) verwendet. Mit einem Vergleich auf einen Wert kontrolliert man dann diese Schleife. Ist ja auch ok. &lt;br /&gt;
&lt;br /&gt;
Muß man den Schleifenzähler aber nun so erweitern, daß man mehr als ein Byte braucht, muß man aufpassen. &lt;br /&gt;
&lt;br /&gt;
'''INC od. DEC   kümmern sich NICHT um das Carry-Bit !'''  &lt;br /&gt;
&lt;br /&gt;
*wer also schreibt&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 clr r0        (manche nehmen das gerne als Register, in dem immer NULL steht. Ist praktisch)&lt;br /&gt;
 ....&lt;br /&gt;
 INC r16&lt;br /&gt;
 adc r17, r0 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
kann sein blaues Wunder erleben. &lt;br /&gt;
&lt;br /&gt;
Da INC bei einem Überlauf einfach wieder bei NULL weitermacht, das Carry-Bit aber nicht anrührt, addieren wir im &amp;quot;ADC&amp;quot; Befehl ein Carry-Bit dazu, das von irgendeinem Befehl vorher übriggeblieben ist.&lt;br /&gt;
&lt;br /&gt;
Für ein sauberes Carry-Bit muß man also auf einen &amp;quot;echten&amp;quot; Additionsbefehl umsteigen. Und was sehen wir ?&lt;br /&gt;
&lt;br /&gt;
'''Es gibt keinen &amp;quot;ADDI&amp;quot; Befehl'''  ( für sowas wie &amp;quot;addi register, 1&amp;quot; )&lt;br /&gt;
&lt;br /&gt;
Wenn wir also kein Register zur Hand haben, in das wir einen 1-er reinschreiben können, müssen wir&lt;br /&gt;
 SUBI register, -1 &lt;br /&gt;
schreiben. Ist klar:  register - (-1) = register + 1, also das, was wir brauchen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
 SUBI    r16, -1        &lt;br /&gt;
 SBCI    r17, -1            (abziehen festen Wert von Register MIT CARRY) &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wen die vielen &amp;quot;-1&amp;quot; wundern: Wir brauchen für eine 16 Bit Zahl (R16:R17) natürlich auch ein 16-Bit-tiges &amp;quot;-1&amp;quot; Literal. Und bei den Vorzeichen-Zahlen haben wir ja festgestellt, daß -1 ja so aussieht:&lt;br /&gt;
 bei  8-Bit 0b11111111                          &amp;amp;HFF&lt;br /&gt;
 bei 16-Bit 0b1111111111111111                  &amp;amp;HFFFF&lt;br /&gt;
 bei 32-Bit 0b11111111111111111111111111111111  &amp;amp;HFFFFFFFF&lt;br /&gt;
Und diese vielen Bits müssen wir auf die einzelnen Subtraktionsbefehlt eben verteilen &lt;br /&gt;
&lt;br /&gt;
*Hinaufzählen mit z.B. 32 Bit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
 SUBI    r16, -1        &lt;br /&gt;
 SBCI    r17, -1            &lt;br /&gt;
 SBCI    r18, -1            &lt;br /&gt;
 SBCI    r19, -1            &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Beim Dekrementieren ist eigentlich nur das Literal ein anderes: statt -1 eben +1, d.h., wir subtrahieren &amp;quot;wirklich&amp;quot;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
 SUBI    r16, 1        &lt;br /&gt;
 SBCI    r17, 0    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*32 Bit ? Richtig:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
 SUBI    r16, 1        &lt;br /&gt;
 SBCI    r17, 0            &lt;br /&gt;
 SBCI    r18, 0            &lt;br /&gt;
 SBCI    r19, 0            &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Ja, es gibt aber doch was für 16 Bit ?'''&lt;br /&gt;
 ADIW  register:register+1 ,  nn  ' nn auf die 16-bit von register:register+1 addieren &lt;br /&gt;
 SBIW  register:register+1 ,  nn  ' nn von den 16-bit von register:register+1 subtrahieren&lt;br /&gt;
(''nn'' kann sein 0 - 63)&lt;br /&gt;
&lt;br /&gt;
Ja, aber das geht nur mit den Registerpaaren R24:R25, R26:R27, R28:R29, R30:R31&lt;br /&gt;
&lt;br /&gt;
Da die Paare R26:R27, R28:R29, R30:R31 aber gleichzeitig die einzigen Pointer-Register sind (dazu kommen wir noch, aber grad in Schleifen braucht man die meist für was anderes)&lt;br /&gt;
&lt;br /&gt;
'''Bleibt eigentlich nur R24:R25 für solche 16-Bit Befehle'''&lt;br /&gt;
&lt;br /&gt;
==Unterprogramme==&lt;br /&gt;
Man sieht ja schon an den bisherigen Beispielen, daß gewisse Befehlsfolgen immer wieder benötigt werden. &lt;br /&gt;
*Typisches Beispiel, was man ja schon beim ersten Lauflicht braucht, sind Verzögerungsroutinen, damit die LEDs nicht gar so schnell funzeln. &lt;br /&gt;
&lt;br /&gt;
Natürlich kann man überall Zählschleifen einbauen, die auf diese Art das Programm gewissermaßen ausbremsen. Aber das verbraucht dann doch mächtig Platz, und auch nur die geringste Änderung in der Befehlsfolge muß man x-mal machen. &lt;br /&gt;
&lt;br /&gt;
Wenn man also diese Befehlsfolge einmal zusammenfaßt und irgendwo außerhalb der Haupt-Programm-Schleife deponiert und mit einem Namen versieht, könnte man doch immer, wenn man sie braucht, diese Folge aufrufen. Ja, aber wie findet man dann wieder zurück, um dort weiterzumachen, wo man grad war?&lt;br /&gt;
&lt;br /&gt;
*Ganz einfach:&lt;br /&gt;
===CALL &amp;amp; RET===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hauptschleife:&lt;br /&gt;
    .....&lt;br /&gt;
    .....&lt;br /&gt;
    CALL   Befehlsfolge&lt;br /&gt;
    .....&lt;br /&gt;
    CALL   Befehlsfolge&lt;br /&gt;
    .....&lt;br /&gt;
    .....&lt;br /&gt;
    JMP   Hauptschleife&lt;br /&gt;
&lt;br /&gt;
Befehlsfolge:&lt;br /&gt;
    ....&lt;br /&gt;
    RET  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das sind zwei listige Erweiterungen von &amp;quot;JMP&amp;quot;:&lt;br /&gt;
*CALL heißt: Erst merken, wo wir grad sind, und dann JMP irgendwohin&lt;br /&gt;
*RET heißt: Mach &amp;quot;JMP&amp;quot; auf die Stelle, die man sich gemerkt hat (und vergiß sie dann)&lt;br /&gt;
&lt;br /&gt;
Super, kann man gut brauchen, aber wie macht der AVR das ? &lt;br /&gt;
&lt;br /&gt;
Dazu braucht der AVR zwei Dinge:  Einen &amp;quot;Stapel&amp;quot; und einen &amp;quot;Stapelzeiger&amp;quot;. &lt;br /&gt;
*Den &amp;quot;Stapelzeiger&amp;quot; (englisch &amp;quot;Stackpointer&amp;quot;) hat er schon in der CPU eingebaut. &lt;br /&gt;
*Der &amp;quot;Stapel&amp;quot; (&amp;quot;Stack&amp;quot;) ist ein Stück Speicher, den uns Bascom bei seiner Initialisierung vom oberen Ende des SRAM abgeknöpft hat. &lt;br /&gt;
**Am Anfang enthält der Stackpointer die oberste Adresse vom Stack&lt;br /&gt;
**Jedesmal, wenn sich der AVR eine Rücksprungadresse merken soll, schreibt er sie dorthin, wo der Stackpointer grad hinzeigt, und zieht dann 2 (so lange ist eine solche Adresse) vom Stackpointer ab.&lt;br /&gt;
**Geht es jetzt ans &amp;quot;RET&amp;quot;, addiert er jetzt diese 2 wieder auf den Pointer, und springt zu der Adresse, die dort steht. &lt;br /&gt;
**Sagt man nun aber nicht &amp;quot;RET&amp;quot;, sondern nochmal &amp;quot;CALL&amp;quot;, schreibt er diese (neue) Adresse nun UNTER die vorherige (und zieht wieder 2 vom Pointer ab). Im Stack stehen jetzt also beide Adressen untereinander, der Stack ist größer geworden &lt;br /&gt;
&lt;br /&gt;
*Der Speicher ist also ein armer Hund: Mit jedem &amp;quot;CALL&amp;quot; knabbern wir von oben was weg, und mit jedem &amp;quot;DIM&amp;quot; (im Bascom) von unten. &lt;br /&gt;
 Erschütternde Tragödien spielen sich ab, wenn sich der Stack und das &amp;quot;DIM&amp;quot; Bereich &lt;br /&gt;
 mal irgendwo in der Mitte treffen, die beiden Bereiche sind sich nämlich spinnefeind.&lt;br /&gt;
Beim Bascom selbst wird es noch viel früher dramatisch: Der verlangt nämlich, daß wir schon beim Programmieren schätzen, wie groß der Stack wohl werden wird ($HWSTACK=nn).&lt;br /&gt;
&lt;br /&gt;
[[Bascom_Inside#Stacks_.26_Frame|Da gibt's ein paar Details dazu]]&lt;br /&gt;
&lt;br /&gt;
===PUSH &amp;amp; POP===&lt;br /&gt;
Mit diesen Befehlen wird der Stack und der Stackpointer direkt angesprochen. Denn &amp;quot;CALL &amp;amp; RET&amp;quot; sind nicht die einzigen, die das brauchen. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 PUSH  register      'Den Inhalt von register an die Adresse schreiben, an die der Stackpointer&lt;br /&gt;
                     'grad hinzeigt, und dann 1 vom Pointer abziehen&lt;br /&gt;
 POP   register      'Auf den Stackpointer 1 addieren, und das Byte dort ins register laden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Typische Anwendung:&lt;br /&gt;
In unserem &amp;quot;Unterprogramm&amp;quot; müssen wir irgendwelche Register verändern, wir wollen aber nicht, daß das Hauptprogramm, das uns aufgerufen hat, was davon merkt&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Befehlsfolge:&lt;br /&gt;
    PUSH  R24&lt;br /&gt;
    LDI   R24, 127&lt;br /&gt;
_Schleife:&lt;br /&gt;
    DEC   R24&lt;br /&gt;
    BRNE  _Schleife&lt;br /&gt;
    POP   R24&lt;br /&gt;
    RET  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nach dem &amp;quot;RET&amp;quot; hat R24 also wieder den Inhalt, den es vorher hatte.&lt;br /&gt;
==Interrupt-Routinen (ISR)==&lt;br /&gt;
ISR = &amp;quot;Interrupt Service Request&amp;quot; = &amp;quot;Unterbrechungs-Behandlung-Anforderung&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Ein AVR besteht ja nicht nur aus CPU und Speicher, sondern auch aus einer Reihe von &amp;quot;Geräten&amp;quot;, die ihre Funktion völlig unabhängig durchführen, wenn man sie mal konfiguriert hat. &lt;br /&gt;
&lt;br /&gt;
Aber immer, wenn sie grad ihre Meß- oder Zählfunktion erledigt haben, wollen sie ihr Ergebnis irgendwie mitteilen und dafür wissen, wie's weitergehen soll. &lt;br /&gt;
&lt;br /&gt;
Wir können dazu mit dem AVR einen Deal machen: &lt;br /&gt;
*Wir schreiben ein Unterprogramm, daß seine dahingehenden Ansprüche erfüllt und sagen dem AVR, wo es im Programmspeicher steht.&lt;br /&gt;
*Und dafür paßt der AVR auf das &amp;quot;Gerät&amp;quot; auf und macht den &amp;quot;Call&amp;quot; auf dieses Unterprogramm völlig automatisch genau dann, wenn es soweit ist. Wir brauchen uns im &amp;quot;normalen&amp;quot; Programm nun nicht mehr darum zu kümmern&lt;br /&gt;
&lt;br /&gt;
Um so einen Handel einzufädeln, ist es wirklich praktisch, wenn wir Bascom hinzuziehen. Der kennt nämlich die meisten AVR-Controllertypen gewissermaßen persönlich und weiß am besten, an welchen Strippen man da bei irgendeinem Controller ziehen muß. &lt;br /&gt;
===Beispiel Timer===&lt;br /&gt;
Sagen wir, wir wolle jede Millisekunde ein Unterprogramm durchführen (lassen). &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$regfile = &amp;quot;m32def.dat&amp;quot;&lt;br /&gt;
$crystal = 8000000&lt;br /&gt;
$hwstack = 48&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kennen wir an sich schon. Aber wenn wir Interrupts verwenden, sollten wir bei &amp;quot;$HWSTACK=&amp;quot; schon ein bißchen was spendieren. &lt;br /&gt;
&lt;br /&gt;
*Jetzt lassen wir Bascom den Timer konfigurieren&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Config Timer0 = Timer , Prescale = 64      'Timer Setup&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lassen wir das Bascom machen. Für uns ist es dann nämlich egal, ob das auf einem Tiny oder einem ATmega128 laufen wird, es schaut immer gleich aus. &lt;br /&gt;
&lt;br /&gt;
*Jetzt der Deal: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   On Timer0 Interrupt_ticker       ' Das ist das Unterprogramm (heißt jetzt aber ISR-Routine) &lt;br /&gt;
&lt;br /&gt;
   Enable Timer0                    ' jetzt machen wir den Timer scharf &lt;br /&gt;
&lt;br /&gt;
   Enable Interrupts                ' Und das ist sozusagen der Hauptschalter für sowas&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Das &amp;quot;normale&amp;quot; Programm: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   $asm&lt;br /&gt;
Hauptschleife:&lt;br /&gt;
   '------------------------------&lt;br /&gt;
   ' Irgendein (Assembler?) Programm, dem der Timer völlig schnuppe ist&lt;br /&gt;
   '------------------------------&lt;br /&gt;
   JMP Hauptschleife &lt;br /&gt;
   $end asm&lt;br /&gt;
&lt;br /&gt;
END&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Und nun die ISR-Routine&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Interrupt_ticker:             &lt;br /&gt;
&lt;br /&gt;
   Timer0 = 131            ' Damit der arme Counter nicht immer von Null weg zählen muß&lt;br /&gt;
                           ' er darf schon mit 131 anfangen&lt;br /&gt;
&lt;br /&gt;
   $asm&lt;br /&gt;
   '------------------------------&lt;br /&gt;
   ' Die befehle, die wir jede mS ausführen wollen &lt;br /&gt;
   '------------------------------&lt;br /&gt;
&lt;br /&gt;
   $end asm&lt;br /&gt;
&lt;br /&gt;
   Return                  ' wieder zurück und weiter im normalen Programm &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die hier verwendeten Werte von &amp;quot;64&amp;quot; für den Prescaler und die &amp;quot;131&amp;quot; für den Counter sind aus der Angabe &amp;quot;$crystal = 8000000&amp;quot; berechnet worden und ergeben Interrupts im Abstand von 1 mS.&lt;br /&gt;
&lt;br /&gt;
==Autor==&lt;br /&gt;
[[User:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
*[[AVR_Assembler_Einf%C3%BChrung|AVR Assembler Einführung]]&lt;br /&gt;
*[[Bascom]]&lt;br /&gt;
[[Kategorie:Software]]&lt;br /&gt;
[[Kategorie:Quellcode Bascom]]&lt;/div&gt;</summary>
		<author><name>-tomas-</name></author>	</entry>

	</feed>