Sie sind auf Seite 1von 150

- Kleiner Bascom AVR Kurs –

(http://halvar.at/elektronik/kleiner_bascom_avr_kurs/)

Vorstellung der wichtigsten Mikrocontroller

Vorab: Also ich kenne mich nur mit den AVR-Mikrocontrollern der Firma Atmel
aus. Und ich programmiere diese mit Bascom (BASIC), deshalb erzähle ich dir
hier nichts von C oder Assembler oder anderen Arten diese Dinger zu pro-
grammieren.

Die AVR-Mikrocontroller gibt es mit 8, 20, 28, 40 und mehr Beinchen (Pins).
Die kleinen µC kommen aus der Familie "ATtiny". Und die für uns interessan-
testen µC sind aus der Familie "ATmega".

Von der ATtiny-Familie sind hauptsächlich die beiden µC ATtiny13 und AT-
tiny45 interessant für uns.

Der ATtiny13 ist sehr klein, aber nicht sehr leistungsfähig. Er kann gut
dafür eingesetzt werden, um kleine Arbeiten zu erledigen für die man vor
ein paar Jahren noch mehrere TTL-Bausteine verwendet hätte.

Den ATtiny45 nimmt man her, wenn man einen kleinen Baustein mit mehr Power
braucht. Der ATtiny45 ist leistungsfähiger als der ATtiny13 und hat mehr
Speicher für Programm und Variablen frei. Damit kann man dann schon komfor-
tabel mit anderen µControllern Verbindung aufnehmen und Aufgaben zugewiesen
bekommen.

Von der ATmega-Familie sind für den Anfang der ATmega8, der ATmega16 und
später der ATmega32 interessant. Es gibt auch µC wie z.B. den ATmega128 und
besser. Aber die sind nicht mehr im DIL-Gehäuse erhältlich und etwas teu-
rer. Deshalb würde ich mich erst dann damit befassen, wenn du die Leistung
wirklich brauchst.

Der ATmega8 ist der typische Einsteiger-Mikrocontroller. Er ist saubillig


(z.B. bei Kessler-Electronic: ATMEGA8-16PU um 1,64 €), ist aber trotzdem
ziemlich leistungsfähig. Er hat 28 Pins, wobei davon bis zu 22 Pins direkt
als Ein-/Ausgänge benutzt werden können. Die meisten Pins sind mehrfach be-
legt, wie man hier im Bild sehen kann.

So, jetzt weißt du zumindest schon mal um welche Mikrocontroller es sich


drehen wird. Ich werde im Laufe der nächsten Tage, Wochen und Monate noch
einiges darüber schreiben.

Wenn du selbständig mit diesen Mikrocontrollern arbeiten und mehr darüber


erfahren möchtest, dann empfehle ich dir zwei Bücher:

Für den Einstieg --> Roland Walter:


AVR Mikrocontroller Lehrbuch
Als Ergänzung --> Claus Kühnel:
Programmieren der AVR RISC Mikrocontroller mit BASCOM-AVR

Meine Meinung zum Buch von Roland Walter und die zugehörige Experimentier-
platine erfährst du hier: Mikrocontroller - Wie anfangen?

Die wichtigsten Datenblaetter

Hier sind die Links zu den ausführlichen Datenblättern der für uns wich-
tigsten AVR-Mikrocontroller:

ATtiny13:
http://www.atmel.com/dyn/resources/prod_documents/doc2535.pdf
ATtiny45:
http://www.atmel.com/dyn/resources/prod_documents/doc2586.pdf
ATtiny2313:
http://www.atmel.com/dyn/resources/prod_documents/doc2543.pdf
ATmega8:
http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf
ATmega16:
http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf

Man muss diese Dinger nicht von vorne bis hinten durchlesen. Aber es ist
doch recht aufschlussreich, mal die ersten Seiten durchzublättern um sich
über die Leistungsfähigkeit der einzelnen Mikrocontroller ein Bild zu ma-
chen.

Und zum Abrunden noch Links zu AVR-Vergleichslisten:

 http://www.atmel.com/dyn/products/param_table.asp?family_id=607
 http://www.loetstelle.net/forum/viewtopic.php?p=12328#12328
 http://akapuma.info/software/vergleichsliste.html

Die Stromversorgung

Die AVR-µC fühlen sich am wohlsten, wenn sie mit stabilisierten 5 Volt ver-
sorgt werden. Da die µC selber nicht viel Strom brauchen, genügt es, wenn
man die Spannung mit einem 7805 stabilisiert.

Ich halte mich dabei meist an diese Seite http://www.strippenstrolch.de/1-


2-11-der-spannungsregler-78xx.html vom Strippenstrolch. Allerdings nehme
ich nicht das oberste Schaltbild, sondern das weiterentwickelte, weiter un-
ten auf der Seite.


 Falls du deine Schaltung auch im Auto einsetzen möchtest, möchte ich
dich auch noch auf einen Eintrag in der Elektronik FAQ hinwei-
sen. http://www.dse-faq.elektronik-kompendium.de/dse-faq.htm#F.23
 Jetzt da die Versorgung geklärt ist, können wir ein bischen näher an
den Mikrocontroller ran rücken. Die AVR-µC haben meist mehrere VCC-
Anschlüsse (Versorgungsspannung). Wenn das der Fall ist, dann müssen
alle diese VCC-Anschlüsse mit Strom versorgt werden. Und wenn es meh-
rere GND-Anschlüsse gibt, dann müssen alle GND-Anschlüsse angeschlos-
sen werden. Es kann sein, dass ein µC auch dann funktioniert, wenn
nicht alle diese Pins angeschlossen werden. Aber unter Last könnte es
sein, dass dadurch der µC nicht mehr korrekt funktioniert oder sogar
zerstört wird.


 Der ATmega8 hat jeweils zwei dieser Anschlüsse. VCC und GND auf der
linken Seite sind für den Digital-Teil. Und AVCC und GND auf der
rechten Seite sind für den Analog-Teil. Der 100 nF Keramikkondensator
soll kleine Störungen ausgleichen. Und wenn der Analog-Teil des µC
ebenfalls verwendet wird, dann sollte man auch den AVCC-Eingang mit
einem 100 nF Keramikkondensator gegen GND schließen. Wenn der Analog-
Teil nicht verwendet wird, dann ist das nicht unbedingt notwendig.
 Falls mit dem µC analoge Messungen durchgeführt werden, dann sollte
der Analog-Teil besonders gut gegen Störungen geschützt werden (z.B.
mit einem Low-Pass-Filter).

 Manche fangen Spannungsschwankungen am Reset-Eingang mit einem Kon-
densator ab und begnügen sich mit dem in den µC eingebauten PullUp-
Widerstand (ca. 50 kOhm). Und andere (wie ich), verlassen sich lieber
auf einen externen 10 kOhm PullUp-Widerstand. In Tirol sagt man zu so
etwas "Kupft wia Gsprunga!" :-)
 Wenn man nicht vor hat, den Mikrocontroller in der Schaltung zu pro-
grammieren, dann kann man auf den Reset-Widerstand auch komplett ver-
zichten und den Reset-Eingang direkt nach VCC anschließen.
 Siehe auch: AVR_Checkliste

Ideale Mindestausstattung

passat2001 schrieb: OK, das ist soweit klar. Kannst du mir jetzt noch sa-
gen, was ich mir an Hardware kaufen muss? Habe einen PC mit USB Anschluss.

Hallo passat2001!

Du brauchst das AVR - Mikrocontroller Lehrbuch von Roland Walter und


das zugehörige Experimentierboard. Beides bekommst du bei Se-
gor: http://cgi.segor.de/user-cgi-bin/sidestep2.pl?foto=1&Q=rowalt&M=1

Dann brauchst du noch ein paar Bauteile, Mikrocontroller und einen Program-
mer (=Gerät um das Programm zum µC zu übertragen). Ich mache es mir einfach
und verlinke direkt dorthin. Und nein, ich bekomme keine Provision. :-)

Absolute Mindestausstattung:
 USB-Programmer: http://shop.myavr.de/index.php?sp=article.sp.php&ar-
tID=42
 ATmega8-Set: http://www.embedit.de/Bausaetze-Boards/Atmel-AVR/AT-
mega8-Starterset-Ohne-Bootloader-ISP10-16-0000MHz.html

Es wäre nicht schlecht ein paar dieser Dinger auf Vorrat zu haben:

 ATtiny13: http://www.embedit.de/Mikrocontroller/AVR/ATtiny/ATtiny13A-
PU.html
 MAX232-Set: http://www.embedit.de/Bausaetze-Boards/Elektronik-Bau-
saetze/MAX232-Set.html
 ATtiny45: http://www.embedit.de/Mikrocontroller/AVR/ATtiny/ATtiny45-
20PU.html
 ATmega8: http://www.embedit.de/Mikrocontroller/AVR/ATmega/ATmega8A-
PU.html
 ATmega16: http://www.embedit.de/Mikrocontroller/AVR/ATmega/ATmega16A-
PU.html
 ATmega16-Set: http://www.embedit.de/Bausaetze-Boards/Atmel-AVR/AT-
mega16-Starterset-Ohne-Bootloader-ISP10-16-0000MHz.html
 5 Volt Spannungsregler: http://www.embedit.de/Aktive-Bau-
teile/ICs/analoge-lineare-ICs/Spannungsregler/Standard/Linearer-Span-
nungsregler-78xx-TO220-1A-7805-5V.html
 IC-Sockel 8 pol.: http://www.embedit.de/Mechanische-Bauteile/So-
ckel/Praezisions-IC-Sockel-2-54mm-8-polig.html
 IC-Sockel 28 pol.: http://www.embedit.de/Mechanische-Bauteile/So-
ckel/Praezisions-IC-Sockel-2-54mm.html
 IC-Sockel 40 pol.: http://www.embedit.de/Mechanische-Bauteile/So-
ckel/Praezisions-IC-Sockel-2-54mm-40-polig.html
 Pfostensteckverbinder 10 pol.: http://www.embedit.de/Mechanische-Bau-
teile/Steckverbinder/Pfostenbuchsen/Pfostenbuchse-2-54mm-10-po-
lig.html
 Elektronik Starterpack: http://www.embedit.de/Bausaetze-Boards/Elekt-
ronik-Bausaetze/Elektronik-Starterset.html

Ideal aber nicht unbedingt notwendig:

 Steckbrett: http://www.reichelt.de/?;ACTION=3;LA=4;ARTICLE=67680
 Atmel Evaluationsboard von Pollin: http://www.pol-
lin.de/shop/shop.php (Pollin hat leider oft Lieferschwierigkeiten;
suche im Shop nach "AVR" oder "ATMEL") ArtNr: 810 038
 Ebenfalls interessant ist das zugehörige "ATMEL Addon-Board" von Pol-
lin: http://www.pollin.de/shop/shop.php ArtNr: 810 053
 Und dieses kleine Addon-Board macht es leichter, das Board mit einem
Steckbrett zu verbinden:http://www.pollin.de/shop/shop.php ArtNr: 810
057

Billige Mindestausstattung
Mir ist aufgefallen, dass sich zwar viele für die Mikrocontrollerprogram-
mierung interessieren, sich aber die von mir beschriebene Basisausstattung
nicht leisten können.

Ich bin von der Ausstattung ausgegangen, die am wenigsten Probleme macht
und dem Lernenden am meisten hilft. Dafür ist nun mal ein Buch notwendig,
das nicht nur die Grundlagen vermittelt und dafür braucht man (außer man
hat wirklich viel Phantasie) auch das zugehörige Experimentierboard. Ich
spreche vom "AVR - Mikrocontroller Lehrbuch".

Natürlich kann man die Mikrocontrollerprogrammierung auch ohne diese Aus-


stattung lernen. Man braucht aber erheblich länger dafür, da man sich die
Informationen aus dem Internet zusammensuchen muss und nicht Kapitel für
Kapitel nur neues lernt.

Weiters kann man zur Not auch auf den von mir vorgeschlagenen mySmartUSB-
Programmer verzichten. Es gibt Programmer für den Parallelport und für den
Seriellen Port, die man sich selber zusammenlöten kann. Wenn man sich das
"ATMEL Evaluations-Board Version 2.0" von Pollin besorgt, dann hat man so
einen Seriellen Programmer sogar mit an Board.

Leider ist Pollin bei diesem Produkt nicht gerade zuverlässig, was die Lie-
ferbarkeit betrifft. Dieses Produkt ist immer wieder ausverkauft. Aber man
kann bei Pollin nachfragen, wann es wieder erhältlich ist und vorbestellen.
Ich musste auf mein Evaluationsboard auch ziemlich lange warten. Und auf
das zugehörige Addon-Board musste ich auch über drei Wochen warten.

Man kann das Programm damit zwar nicht direkt aus Bascom heraus zum µC
übertragen, aber die von Bascom erzeugten HEX-Dateien können mit dem Pro-
gramm "PonyProg" http://www.lancos.com/prog.html zum µC übertragen werden.

Das ist zwar für Vielprogrammierer nicht angenehm und auf keinen Fall
schnell, aber es ist billig. Anzumerken ist, dass man eine echte, in den
Computer eingebaute, Hardware-RS-232-Schnittstelle dafür braucht. USB/COM-
Adapter funktionieren nicht.

Dann gibt es auch noch die Möglichkeit, AVR-Mikrocontroller über den Paral-
lelport zu flashen. Wie man dafür einen billigen Programmer aufbaut, habe
ich in Motorsteuerung mit PC und ATtiny13 im Kapitel Parallelport-Program-
mer bauen erklärt.

Ich hoffe, damit mehr Hobby-Elektroniker zum Einstieg in die µC-Programmie-


rung bewegen zu können.

Interessante Links:

 http://www.roboternetz.de/phpBB2/viewtopic.php?t=38913
 http://www.loetstelle.net/forum/viewtopic.php?p=11163#11163

Programmer (Programmiergeraet)
Wenn ein Programm fertig programmiert wurde, dann wird es kompiliert. Das
heißt, der Bascom-Quellcode, den wir geschrieben haben, wird in ein vom µC
ausführbares Maschinencode-Programm umgewandelt. Dieses Programm kann als
HEX-Datei oder als BIN-Datei vorliegen.

Um dieses Programm in den µC übertragen zu können, braucht man einen so ge-


nannten Programmer (Programmiergerät). Das kann ein einfaches Kabel für den
Anschluss an den Parallelport mit ein paar Widerständen und ein paar Dioden
sein. Das kann ebenso ein mit ein paar Dioden und Widerständen versehenes
Kabel für den Anschluss an den "Seriellen Port" (RS-232) sein. Das kann
aber auch ein Gerät für den USB sein. Es gibt viele verschiedene Programmer
für die verschiedensten Anschlüsse.

Aber warum nimmt dann nicht jeder ein einfaches Parallelportkabel, wenn es
doch damit funktionieren soll? Oder warum nicht einfach das Kabel für den
"Seriellen Port"? -- Die Sache ist die, dass viele Computer keinen Paral-
lelport mehr haben. Es gibt zwar USB-Parallelport-Adapter, aber mit denen
kann man die µC nicht programmieren, da der Datenaustausch in beide Rich-
tungen oft nicht richtig funktioniert und die Hi-Low-Level meist keine ein-
heitliche Spannung haben.

Es gibt Parallelport-Programmer, die zumindest das Problem der niedrigen


Parallelport-Spannung ein wenig entschärfen. Den Bauplan für einen STK200-
kompatiblen Programmer für den Parallelport findest du un-
ter http://www.mikrocontroller.net/articles/STK200.

Es geht noch weiter. Seit Windows 2000 kann man nicht mehr direkt von einem
Programm aus auf den Parallelport zugreifen. Man kann das umgehen, indem
man einen speziellen Treiber dafür installiert. Aber leider werden diese
Treiber nicht aktiv weiterentwickelt, so dass diese Treiber schon seit
Windows XP teilweise Schwierigkeiten machen oder sogar überhaupt nicht
funktionieren. Fazit: Der Parallelport scheidet aus, wenn man kein Gefri-
ckel will. Er ist aber eine billige Alternative, wenn man sparen muss und
noch einen richtigen, eingebauten Parallelport im Computer hat.

Tipp

Diesen STK200 Parallelport-Programmer habe ich nachgebaut und muss geste-


hen, dass er bei mir ziemlich gut läuft. Er taugt also auf jeden Fall als
billiger Programmer, wenn man sich keinen USB-Programmer leisten kann und
noch einen Parallelport in seinem Computer eingebaut hat.

Dieser Programmer ist so billig, dass man es verschmerzen kann, falls einem
das Betriebssystem den Spaß verdirbt und den Parallelport nicht frei gibt.
In so einem Fall kann man ja immer noch einen USB-Programmer kaufen.

Weit mehr Computer haben noch einen "Seriellen Port" (RS-232 oder auch COM
genannt), der nicht wie der Parallelport vom Betriebssystem abgeblockt
wird. Also scheint der COM-Port gar nicht so schlecht dafür geeignet zu
sein. Und die benötigte Hardware begrenzt sich auf ein Kabel, ein paar Dio-
den, Transistoren und Widerstände. Aber ein paar Probleme gibt es damit
doch. Diese Art der Übertragung ist nicht sehr gut in Bascom integriert,
was das Arbeiten erschwert. Außerdem funktionieren die meisten, einfachen
"Seriellen" Programmer nur mit echten, in den Computer eingebauten COM-
Ports.
Leider gibt es Probleme mit USB/COM-Adaptern. Diese stellen oft nicht die
gewünschten Spannungen für die Programmer zur Verfügung. Oft sind auch nur
die Pins RX und TX durchgeschaltet, was zu wenig für die einfachen "Seriel-
len" Programmer ist.

Es gibt auch "Serielle" Programmer mit eingebauten Mikrocontrollern. Diese


können auch an USB/COM-Adapter angeschlossen werden und die daran ange-
schlossenen µController mit voller Geschwindigkeit flashen. Aber mit denen
habe ich mich nie befasst und kann nichts dazu schreiben.

Die folgenden Links führen dich zu Seiten, die dir mehr über über ein paar
dieser einfachen Programmer erzählen und wie man sich selbst so einen Pro-
grammer basteln kann.

 http://www.kreatives-chaos.com/artikel/avr-programmieradapter
 http://www.roboternetz.de/wissen/index.php/AVR-ISP_Programmierkabel
 http://rowalt.de/mc/avr/progd.htm
 http://rowalt.de/mc/avr/avrbuch/AvrBoard.pdf

Für heutige Computer empfehle ich USB-Programmer.

USB wird nicht so schnell aussterben und die USB-Programmer werden immer
billiger. So gibt es mit dem mySmartUSB einen Programmer, der -- an den
Computer angeschlossen -- einen COM-Port simuliert und sehr einfach von
Bascom aus genutzt werden kann. Also ideal für Einsteiger.

Diesen mySmartUSB-Programmer bekommt man direkt bei myAVR oder von Conrad.
Wenn man den Programmer geliefert bekommt, dann fehlt nur noch der zehnpo-
lige Wannenstecker, den man noch einlöten muss. Mit dabei ist ein kurzes
Flachbandkabel mit zwei zehnpoligen Pfostensteckverbindern. Es hat sich
eingebürgert, µC-Schaltungen mit einem Wannenstecker auszustatten, über den
die µC im System programmiert werden können.

Die AVR-Mikrocontroller müssen also nicht jedes mal ausgebaut werden, um


ein neues Programm zu übertragen. Es genügt, wenn man die Leitungen "Re-
set", "MOSI", "MISO", "SCK", "GND" und "VCC" über einen Stecker nach außen
führt. Diesen Stecker kennzeichnet man üblicherweise mit "ISP", was für "In
System Programming" steht. Auf die Bedeutung von "MOSI", "MISO" und Co.
werde ich später noch eingehen. Aber zuerst kümmern wir uns um die Hard-
ware. Es ist nämlich so, dass Roland Walter in seinem Experimentierboard
nicht diesen Wannenstecker, sondern eine sechspolige SIL-Leiste verwendet.
Das ist zwar schön platzsparend, aber man muss sich einen Adapter zusammen-
löten um mit dem mySmartUSB auch dieses Experimentierboard anschließen zu
können.

10-poliger und 6-poliger ISP-Stecker (Atmel):


http://www.mikrocontroller.net/articles/AVR_In_System_Programmer

6-poliger, einreihiger Stecker den Roland Walter verwendet:

http://rowalt.de/mc/avr/avrbuch/AvrBoard.pdf

Beim Übertragen der Daten kommuniziert der Programmer mit dem µC normaler-
weise über die SPI-Schnittstelle (Serial Peripheral Interface). Diese SPI-
Schnittstelle arbeitet mit drei Leitungen. Wenn mehr als zwei Geräte mitei-
nander kommunizieren, dann ist noch eine zusätzliche Leitung zum Auswählen
des Empfängers im Spiel. Aber das ist jetzt nicht so wichtig.

Die SCK-Leitung (SPI Clock) gibt den Takt an. Einer der Kommunikations-
partner ist immer der Master. Dieser bestimmt den Takt. Der andere Kommuni-
kationspartner ist der Slave.

Die MOSI-Leitung (Master Out/Slave In) überträgt Daten vom Master zum
Slave.

Die MISO-Leitung (Master In/Slave Out) überträgt Daten vom Slave zum Mas-
ter.

Dieses SPI kann dazu benutzt werden um Daten und Anweisungen von einem µC
an andere µC weiter zu geben. Es wird auch zum Übertragen des Programms vom
Computer zum µC verwendet. Der einzige Unterschied ist, dass der Programmer
vorher den Reset-Eingang des µC nach GND zieht. Das ist das Zeichen für den
µC, dass es sich bei der Übertragung nicht nur um normale Daten, sondern um
das Programm handelt. Dieses wird dann in den eingebauten Flash-Speicher
geschrieben.

Manche µC verwenden nicht die SPI-Pins zum Flashen. So ein Sonderfall ist
z.B. der ATmega128. Das ist aber für uns noch nicht so wichtig, da der AT-
mega128 eine Liga über dem ATmega8 oder dem ATmega16 spielt.

Siehe auch: AVR Hardware Design Considerations

PS: Nicht verwechseln: SPI (Serial Peripheral Interface) und ISP (In System
Programing)
STK200 Programmer

Um eine Empfehlung abgeben zu können, habe ich heute den STK200 Programmer
dieser Seite http://www.mikrocontroller.net/articles/STK200 nachgebaut. Ich
habe dafür den 74244 als Treiber verwendet.

Ich bin sehr zufrieden mit dem Ergebnis. Der Programmer funktioniert auch
auf meinem neuen PC (auf dem der SP12 nicht funktioniert) einwandfrei. :-)

Im Bascom muss man als Programmer den STK200 einstellen. Auf die anderen
Bascom-Einstellungen werde ich in den nächsten Beiträgen eingehen.
Und sogar die Fuse-Bits lassen sich damit direkt aus Bascom heraus einstel-
len.
Neulich habe ich, im Laufe eines Projektes, diesen STK200 nachgebaut. Das
heißt, mit Schaltplan, Lochrasterplan und Fotos vom Aufbau. Zu finden ist
der Bericht im Artikel über die "Motorsteuerung mit PC und ATtiny13".

Also, für alle mit Parallelport: Ich würde diesen Programmer auf jeden Fall
einmal ausprobieren.

Links:

 http://www.mikrocontroller.net/articles/STK200
 http://www.mikrocontroller.net/articles/AVR_In_System_Programmer

mySmartUSB einrichten

Abb. mySmartUSB MK2 (Programmer und Bridge) (http://shop.myavr.de/in-


dex.php?sp=article.sp.php&artID=42)

Ich möchte noch einmal auf den mySmartUSB-Programmer eingehen, bevor ich
mich auf Bascom stürze. Der mySmartUSB muss nämlich installiert, upgedated
und eingestellt werden bevor man los legen kann.

Zum mySmartUSB wird eine CD mit Software mitgeliefert. Aber bei mir war es
so, dass die Programme auf dieser CD nicht so aktuell waren, wie die die
man im Internet herunterladen kann.

Wichtig
Der mySmartUSB darf vor der Installation der Treiber nicht angeschlossen
werden. Für die Installation benötigt man Administratorrechte.

Treiber, Programme und Informationen

Die zugehörigen Treiber, Programme und Informationen kann man unter dieser
URL herunterladen: http://shop.myavr.de/index.php?sp=download.sp.php
Suche auf der Download-Seite nach diesen Dateien:

USB-Treiber für das myAVR Board 2 USB und mySmartUSB

Dieser Treiber erstellt einen virtuellen COM-Port, über den auf den mySmar-
tUSB zugegriffen werden kann. Dieser Treiber ist wichtig und muss korrekt
installiert werden. Entpacke die heruntergeladene ZIP-Datei in einen Ordner
und lies die mitgelieferte PDF-Datei "Update der mySmartUSB Trei-
ber_d_eng.pdf" gründlich durch und halte dich bei der Installation so gut
wie möglich an die Anleitung.

Wichtig
Verwende beim Einstellen des COM-Ports nur Adressen zwischen 1 und 4. An-
sonsten kann man mit einigen Programmen nicht auf den Programmer zugreifen.
Verwendbar sind also nur COM1, COM2, COM3 und COM4.
Firmware für USB-Programmer

Beim Schreiben dieses Beitrages ist die Version 2.5 aktuell. Bevor das
Firmwareupdate durchgeführt werden kann, muss der USB-Treiber installiert
werden. Entpacke die Zip-Datei und führe das darin enthaltene Programm aus.
Das Programm führt durch die Installation.

myAVR Workpad PLUS, Demo

Dieses Programm ist zwar nur eine Demo-Version, aber damit kann man die
Fuse-Bits der µC einstellen. Das funktioniert zwar auch mit anderen Pro-
grammen, aber bei weitem nicht so komfortabel. Das Programm kann man in-
stallieren und nach dem Start einige Minuten lang nutzen. Lange genug um
damit die Einstellungen tätigen zu können.

myAVR ProgTool

Damit kann man Programme zum µC übertragen. In der nächsten Programmversion


soll man endlich auch Kommandozeilenparameter beim Start des Programms an-
geben können. Damit könnte dieses Programm auch komfortabel aus Bascom her-
aus verwendet werden.

Kommandozeilen-Tool für mySmartUSB

Damit schaltet man den mySmartUSB in die verschiedenen Modi. Dieser kann
nämlich nicht nur als Programmer arbeiten. Man kann ihn auch dazu verwenden
um direkt mit den µControllern zu kommunizieren. Aber darauf gehe ich nicht
weiter ein. Wichtig ist, nur, dass man dieses Tool braucht um den Program-
mer in den "Programmier-Modus" zu schalten. Bitte gleich ausprobieren und
den Programmer in den Programmier-Modus stellen. Der Programmer befindet
sich im Programmier-Modus, wenn die rote LED leuchtet.

Produktbeschreibung zum USB Programmer mySmartUSB

durchlesen

Technische Beschreibung zu mySmartUSB

durchlesen
Der Einstieg für die Programmierung des myAVR Boards mit BASCOM

Durchlesen

Bascom Einstellungen

Na, dann kommen wir jetzt zu Bascom.

Bascom ist das Programm mit dem die Programme für den µC geschrieben werden. Die Vollversion kostet 80
Euro und ermöglicht es dir, auch Programme über 4 kByte Größe zu schreiben. Aber für den Anfang genügt die
Demo-Version. Damit kann man schon einiges anstellen. Erst recht, wenn man bedenkt, dass der Flash-Speicher
eines ATtiny45 auch nur 4 kByte groß ist.

Das Programm Bascom-AVR kann man sich bei MCS-Electronics unter http://mcselec.com/index.php?op-
tion=com_content&task=view&id=14&Itemid=41 herunterladen.

Programmoberfläche

Im linken Bereich kann man das Programm schreiben und im rechten Bereich kann man mehrere Hilfsmittel ein-
blenden. Ich habe dort oft die Pinbelegung des Chips eingeblendet. Es wird ein Bild des µC angezeigt, sobald
aus dem Programm hervor geht, welcher µC programmiert wird. Das hilft enorm bei der Auswahl der richtigen
Pins.

Hilfe

Die wichtigste Info kommt gleich vorab. Wenn man den Cursor (blinkender Strich) auf einen Befehl setzt und
dann die F1-Tastedrückt, dann bekommt man die Hilfe zu diesem Befehl angezeigt. Bitte gleich ausprobieren
und merken.

Wichtig
Unbedingt merken >>F1-Taste<<!
Uns so sieht dann z.B. so eine aufgerufene Hilfe-Seite aus:

--> gleich ausprobieren. :-)

Einstellungen

Zu den Einstellungen kommt man über das Optionen-Menü. Und so sehen die Einstellungen bei mir aus:
Compiler - Chip

Compiler - Output
Compiler - Communication

Compiler - I2C, SPI, 1Wire


Compiler - LCD

Compiler - Options
Communication

Environment - Editor
Environment - Font

Environment - IDE
Environment - PDF

Simulator
Programmer

Monitor
Printer

Jetzt, da Bascom installiert ist, der Programmer funktioniert und geklärt wurde, wie der
AVR-µC angeschlossen werden muss, könnte man schon mal mit einer kleinen "Hallo
Welt"-Anwendung los legen.

Ich möchte dich aber vorher nochmal kurz mit ein wenig Theorie langweilen. ;-) Es geht
darum, wie so ein AVR-Controller die Aussenwelt sieht und wie man diese beeinflussen
kann. Es geht darum, wie man einen Pin als Eingang oder als Ausgang verwenden kann.

In den AVR-µControllern wird jede Information im Speicher abgelegt. Ob an einem Pin


ein Signal anliegt oder ob ein Analog-Digital-Wandler herausfinden soll, wieviel Volt an
einem Pin anliegen -- alles steht im Speicher. Man kann diesen Speicher auslesen und
man kann in diesen Speicher schreiben. Um sich die Adressen der Speicherbereiche nicht
merken zu müssen, wurden den einzelnen Speicherbereichen Namen gegeben. Diese
Speicherbereiche nennt man REGISTER. Ein Register enthält acht einzelne Ja/Nein-
Werte (Bit). Deshalb nennt man die AVR auch 8-Bit Mikrocontroller.

8 Bit = 1 Byte = 1 Register

Und auch für die Pins gibt es solche Register. Es wurden immer bis zu acht Pins zusam-
mengelegt und können über ein dafür festgelegtes Register angesprochen werden. Diese
Register nennt man I/O-Register (Input/Output).

Der ATmega8 hat drei solche Register die für die Pins zuständig sind. Das sind PORTB,
PORTC und PORTD. Und das muss man sich noch merken: Wenn man aus diesen I/O-Re-
gistern etwas lesen möchte, dann spricht man sie nicht mit PORTx, sondern mit PINx an.
PINB, PINC und PIND.
In diesem Bild wird statt PORT/PIN einfach "P" verwendet.

Die einzelnen Pins werden mit 0 beginnend numeriert. So ist PORTB.0 die Adresse mit
der man den Status des Pins PB0 setzen kann. Und mit PINB.0 liest man aus, ob am Pin
PB0 ein HIGH oder LOW anliegt. Ach ja, das hätte ich fast vergessen: Wenn ich von
HIGH und LOW schreibe, dann meine ich damit, dass entweder Spannung an den Pin ge-
legt wird (HIGH) oder der Pin gegen GND gezogen wird (LOW).

Wenn ich in Bascom PB0 auf HIGH setzen möchte, dann schreibt man:

PORTB.0 = 1

Mit diesem Befehl setzt man PB0 auf LOW:

PORTB.0 = 0

Und wenn ich z.B. PB1 auf HIGH setzen möchte, wenn PB0 LOW ist und umgekehrt, dann
sieht das so aus:

IF PINB.0 = 0 THEN
PORTB.1 = 1
ELSE
PORTB.1 = 0
END IF

Bevor man allerdings mit einem der Pins arbeitet, muss man festlegen, welche Pins eines
PORTs Eingänge und welche Ausgänge sind. Wenn man nichts verändert, dann sind nach
dem Start des Programms alle Pins Eingänge.

Ob ein Pin des PORTB ein Eingang oder ein Ausgang ist, wird im Register DDRB gespei-
chert. Jeder Pin entspricht einem Bit dieses Registers. So gibt es für den ATmega8 noch
die Register DDRC und DDRD in denen die Ausrichtungen der Pins gespeichert werden.
DDR steht hier für "Data Direction Register".
Diese Anweisung kennzeichnet PB1 und PB2 als Ausgänge. Alle anderen Pins bleiben Ein-
gänge:

DDRB = &B00000110

Erst jetzt kann mit PORTB.1 = 1 der Pin PB1 auf HIGH gesetzt werden.

Es gibt noch eine andere Art, einen Pin als Eingang oder als Ausgang zu kennzeichnen.

CONFIG PINB.0 = INPUT


CONFIG PORTB.1 = OUTPUT
CONFIG PORTB.2 = OUTPUT

Manchmal ist es einfacher, wenn man es so macht und manchmal ist es einfacher, wenn
man direkt in das DDRx-Register schreibt. Das muss man selber entscheiden.

Mit diesem Wissen könnte man also schon eine LED anschließen und ein- bzw. ausschal-
ten. Jetzt fehlt uns nur noch das Grundgerüst des Programms.

Minimales Grundgerüst eines Programms:

$regfile = "M8def.dat" 'es handelt sich um einen ATmega8


$crystal = 1000000 'der eingebauter RC-Oszillator läuft mit 1 Mhz
$hwstack = 100 'im Speicher werden für den Hardware-Stack 100
Byte reserviert
$swstack = 100 'im Speicher werden für den Software-Stack 100
Byte reserviert
$framesize = 100 'im Speicher werden für den Frame 100 Byte re-
serviert

Ddrb = &B00000010 'PB1 ist Ausgang. Die anderen Pins sind Ein-
gänge
Portb.1 = 1 'Signal auf PB1 einschalten (HIGH)

End 'Ende des Programms

Dieser Code schaltet PB1 auf HIGH. Damit könnte an PB1 eine LED angeschlossen wer-
den und sie würde leuchten.

Ein Pin des ATmega8 kann mit bis zu 40 mA belastet werden. Allerdings darf die Gesamt-
belastung des ATmega8 (je nach Gehäusetyp) nicht über 200 mA steigen. Es gibt da
auch noch Unterschiede zwischen den einzelnen Anschlüssen. Wer es genau wissen will,
sollte ins Datenblatt schauen. Eine LED würde also leuchten. Aber um den µC zu scho-
nen, sollte man einen Pin nicht immer unter Vollast betreiben. Wenn schon direkt eine
LED angeschlossen werden soll, dann bitte eine Low Current LED.
Im nächsten Beitrag geht es mit diesem "Hallo Welt"-Programm weiter. Dann erkläre ich
wie man das Programm eingibt, abspeichert, kompiliert und zum µC überträgt. Ich werde
dann auch genauer auf das Programm-Grundgerüst eingehen und einen Schaltplan für
den Anschluss einer LED zeichnen.

In diesem Beitrag möchte ich erklären, wie man das Programm eingibt, speichert, kompi-
liert und zum µC überträgt.

Starte Bascom-AVR. Über das Menü "Datei/Neu" wird ein neues Programm erstellt. Damit
hast du in der linken Seite schon mal ein weißes Fenster in das du den Programmcode
eingeben kannst. Gib diesen Code ein:

$regfile = "M8def.dat" 'es handelt sich um einen ATmega8


$crystal = 1000000 'der eingebaute RC-Oszillator läuft mit 1 Mhz
$hwstack = 100 'im Speicher werden für den Hardware-Stack 100
Byte reserviert
$swstack = 100 'im Speicher werden für den Software-Stack 100
Byte reserviert
$framesize = 100 'im Speicher werden für den Frame 100 Byte re-
serviert

Ddrb = &B00000010 'PB1 ist Ausgang. Die anderen Pins sind Ein-
gänge
Portb.1 = 1 'Signal auf PB1 einschalten (HIGH)

End

Speichere das Programm unter dem Namen "hallo_welt_v01.bas" in den Ordner Eigene
Dateien\Bascom-Programme\Hallo Welt\ ab.
Wenn du nun auf das Symbol zum Kompilieren klickst, dann wird das Programm in
Maschinensprache umgewandelt.

Im Ordner Eigene Dateien\Bascom-Programme\Hallo Welt\ befinden sich nun ein paar


Dateien mehr. Die HEX-Datei und die BIN-Datei enthalten jetzt das fertige Programm.
Dieses muss jetzt nur noch zum µC übertragen werden.

Dafür müssen wir den µC über den Programmer an den Computer anschließen. Ich
nehme für solche Testaufbauten gerne ein Steckbrett (Breadboard). Damit kann man die
Schaltung recht einfach aufbauen und testen. Wenn man schon ein Experimentierboard
wie das von Roland Walter oder das "ATMEL Evaluations-Board Version 2.0" von Pollin
hat, dann sind solche Aufbauten noch viel einfacher. Dann muss man sich nicht mehr um
so grundlegende Dinge wie Stromversorgung und Stecker für den Anschluss des Pro-
grammers kümmern.

Diese Schaltung sieht auf einem Steckbrett so aus:


Wenn der mySmartUSB in den Programmiermodus geschalten wurde, dann leuchtet die
rote LED auf dem Programmer auf (das wurde in einem der vorherigen Beiträge erklärt).

Mit einem Klick auf die grüne Schaltfläche sollte das Übertragen des Programms be-
ginnen. Und wenn alles geklappt hat, dann sollte jetzt auch die Low Current LED leuch-
ten. "Hallo Welt!" :-)

Dieses Beispiel kann man natürlich auch ein wenig ausbauen:

'es handelt sich um einen ATmega8


$regfile = "M8def.dat"

'der eingebaute RC-Oszillator läuft mit 1 Mhz


$crystal = 1000000

'im Speicher werden für den Hardware-Stack 100 Byte reserviert


$hwstack = 100

'im Speicher werden für den Software-Stack 100 Byte reserviert


$swstack = 100

'im Speicher werden für den Frame 100 Byte reserviert


$framesize = 100

'PB1 ist Ausgang. Die anderen Pins sind Eingänge


Ddrb = &B00000010

Do
'PB1: Signal einschalten (HIGH)
Portb.1 = 1

'Eine Sekunde Warten


Wait 1

'PB1: Signal ausschalten (LOW)


Portb.1 = 0

'Eine Sekunde Warten


Wait 1
Loop

'Ende des Programms


End

Und hier sieht man auch wie ein Programm normalerweise aufgebaut ist. Jedes Pro-
gramm hat eine MainLoop -- eine Hauptschleife. Das Programm in der Hauptschleife
wird so lange wiederholt, bis entweder Reset gedrückt wird oder der µC keinen Strom
mehr bekommt.

Last ueber einen Transistor anschliessen (z.B. Relais)

Wenn man nicht nur eine Low Current LED anschließen möchte, dann muss man das Aus-
gangssignal verstärken. Das Einfachste ist, wenn man einen Transistor zum Verstärken
verwendet.

Folgender Schaltplan zeigt auf, wie man eine normale 20 mA LED an den µC anschließen
kann.
R3 habe ich im Schaltplan mit "1k - 10k" angegeben. Das hat einen Grund.

Ist ein als Ausgang definierter Pin des µC auf LOW geschaltet, dann zieht dieser Pin ge-
gen GND. Das hat den Vorteil, dass man die Basis des Transistors nicht zusätzlich über
einen Widerstand gegen GND ziehen muss um diesen zu entstören. Bei einem 2N2222-
Transistor genügt ein 10k-Widerstand um die LED in voller Stärke leuchten zu lassen.
Aber bei 10k ist die Basis des Transistors nicht so stark entstört wie es bei einem 1k-Wi-
derstand der Fall wäre.

Nimmt man 10k als Basiswiderstand, dann braucht die Schaltung sehr wenig Strom, ist
aber nicht so gut entstört. Nimmt man 1k als Basiswiderstand, dann braucht die Schal-
tung mehr Strom, ist dafür aber gut entstört.

Man muss also von Fall zu Fall entscheiden, wie wichtig die Entstörung der Transistor-Ba-
sis ist und kann mit diesem Wissen zwischen 1k und 10k variieren. Außerdem wird es ja
nicht so oft vorkommen, dass man mit dem Finger direkt an die Transistor-Basis greift. ;-
)

Und so kann man auch größere Last an den µC anschließen: http://www.strippen-


strolch.de/1-6-8-relais-mit-kleinem-strom-ansteuern.html
Je mehr Strom die Last zieht, desto kleiner muss der Basiswiderstand sein. 10k ist bei ei-
nem Relais schon ziemlich hoch. Vielleicht solltest du bei einem Relais lieber gleich einen
1k Widerstand zwischen den µC-Ausgang und die Transistor-Basis schalten. Und vergiss
nicht, in der Nähe des Relais eine Freilaufdiode unterzubringen.

Schalter an den Mikrocontroller anschliessen Teil1

Letztens habe ich erklärt, wie man über ein digitales Signal des µC eine LED oder ein Re-
lais ansteuern kann. Heute ist es so weit, dass ich in die andere Richtung gehen möchte.
Wie schließt man einen Schalter an?

Es gibt zwei grundsätzliche Möglichkeiten. Entweder der Schalter zieht gegen VCC oder er
zieht gegen GND. Beides lässt sich detektieren. Und was passiert wenn der Schalter nicht
gedrückt wird? Das wird oft nicht bedacht. Und erst heute (wie peinlich) ist es mir selber
passiert, dass ich mich nicht darum gekümmert habe. Und statt 10 Hz hatte ich schwan-
kende 2600 Hz an einem Eingang und wusste nicht warum. :-)

Wenn der Schalter gegen VCC zieht, dann sollte man den Eingang über einen PullDown-
Widerstand gegen GND ziehen. So ist sicher gestellt, dass am Eingang LOW anliegt und
erst wenn der Schalter gedrückt wird zu HIGH wird. Zieht der Schalter gegen GND, dann
sollte ein PullUp-Widerstand gegen VCC ziehen.

Und wenn man Bauteile sparen möchte, dann kann man den in den µC eingebauten Pul-
lUp-Widerstand softwaremäßig aktivieren. Es gibt also für jeden möglichen Eingangspin
auch die Möglichkeit, einen PullUp-Widerstand zu aktivieren. Dieser ist bei den AVRs zwi-
schen 20 und 50 kOhm groß.

So sieht es aus, wenn der Schalter gegen VCC zieht:

So sieht es aus, wenn der Schalter gegen GND zieht:


Zwar hat der Reset-Eingang auch einen PullUp-Widerstand eingebaut, aber beim Reset
machen wir eine Ausnahme und beschalten ihn trotzdem zusätzlich noch mit einem ex-
ternen PullUp-Widerstand.

Es gibt mehrere Möglichkeiten, um festzustellen, ob der Taster gedrückt wird oder nicht.
Die einfachste ist die, den Status des Eingangspins abzufragen. Dieses kleine Beispielpro-
gramm steuert zwei LEDs an. Im Ausgangszustand leuchtet die LED1. Drückt man den
Taster dann leuchtet die LED2.

Dieses Beispiel geht davon aus, dass der Taster den Eingangspin gegen VCC zieht.

$regfile = "M8def.dat"
$crystal = 1000000
$hwstack = 100
$swstack = 100
$framesize = 100

'LED1 an PB1
Led1 Alias Portb.1
Config Led1 = Output
'LED2 an PB2
Led2 Alias Portb.2
Config Led2 = Output

'TASTER1 an PD2
Taster1 Alias Pind.2
Config Taster1 = Input

Do
If Taster1 = 1 Then
Led1 = 0
Led2 = 1
Else
Led1 = 1
Led2 = 0
End If
Loop

End

Und so sieht es aus, wenn man mit dem Taster den Eingangspin gegen GND zieht. Es
wird der interne PullUp-Widerstand aktiviert um den Eingang in einem definierten Zu-
stand zu halten. Das Einschalten des internen PullUp-Widerstand: Zuerst wird der Pin als
Eingang definiert und dann setzt man den Pin auf HIGH. So als wäre dieser ein Ausgang.
Aber statt dass am Pin ein HIGH angelegt wird, wird damit der PullUp-Widerstand akti-
viert.

$regfile = "M8def.dat"
$crystal = 1000000
$hwstack = 100
$swstack = 100
$framesize = 100

'LED1 an PB1
Led1 Alias Portb.1
Config Led1 = Output
'LED2 an PB2
Led2 Alias Portb.2
Config Led2 = Output

'TASTER1 an PD2
Taster1 Alias Pind.2
Config Taster1 = Input
Portd.2 = 1 'PullUp-Widerstand einschalten

Do
If Taster1 = 0 Then
Led1 = 0
Led2 = 1
Else
Led1 = 1
Led2 = 0
End If
Loop

End

Du hast sicher schon bemerkt, dass noch etwas hinzugekommen ist. Mit ALIAS kann man
einer Variable einen zusätzlichen Namen geben. Schlüssige Namen erleichtern das Pro-
grammieren und sollten oft eingesetzt werden.

Versuche doch mal zum Test diese Zeile aus dem Code raus zu nehmen:

Portd.2 = 1 'PullUp-Widerstand einschalten

Drücke nach dem Übertragen des Programms mehrmals den Taster. Dann weißt du was
es bedeutet, den PullUp-Widerstand zu vergessen.

Variablen

In diesem Beitrag geht es wieder um reine Theorie. Aber bitte trotzdem durchlesen, da
dieses Thema sehr wichtig ist. Es geht um Variablen.
Was ist eine "Variable"? In der µC-Programmierung ist eine Variable ein Name für einen
Speicherbereich. Außerdem ist in Bascom an die Variable auch noch der Datentyp gebun-
den, der in dem Speicherbereich gespeichert werden kann.

In Bascom gibt es mehrere mögliche Datentypen.

 BIT [1]: besteht aus nur einem Bit und kann 0 oder 1 enthalten
 BYTE [2]: besteht aus 8 Bit und kann Ganzzahlen von 0 bis 255 enthalten
 WORD: besteht aus 16 Bit und kann Ganzzahlen von 0 bis 65535 enthalten
 INTEGER: besteht aus 16 Bit und kann Ganzzahlen von -32768 bis +32767 ent-
halten
 LONG: besteht aus 32 Bit und kann Ganzzahlen von -2147483648 bis
2147483647 enthalten
 SINGLE [3]: besteht aus 32 Bit und kann Fließkommazahlen von 1,5 x 10^–45
bis 3,4 x 10^38 enthalten
 DOUBLE [4]: besteht aus 64 Bit und kann Fließkommazahlen von 5,0 x 10^–324
bis 1,7 x 10^308 enthalten
 STRING: Dieser Datentyp kann Text mit der Länge von bis zu 254 Zeichen ent-
halten. Dafür wird im Speicher immer ein Zeichen mehr als die Länge des Textes
benötigt, da der Text immer mit einem 0-Byte abgeschlossen wird.

[1] "Bit" im Wikipedia: http://de.wikipedia.org/wiki/Bit

[2] "Byte" im Wikipedia: http://de.wikipedia.org/wiki/Byte

"Einfache Genauigkeit" im Wikipedia: http://de.wikipedia.org/wiki/Einfache_Genau-


[3]
igkeit

"Doppelte Genauigkeit" im Wikipedia: http://de.wikipedia.org/wiki/Doppelte_Genau-


[4]
igkeit

Man muss Bascom mitteilen, welche Variablen mit welchem Datentyp man benutzen
möchte. Das passiert mit dem Befehl DIM und sieht z.B. so aus:

DIM my_bit_var as BIT


DIM my_byte_var as BYTE
DIM my_byte_array(3) as BYTE
DIM my_word_var as WORD
DIM my_single_var as SINGLE
DIM my_string_var as STRING * 10

Die Besonderheit des STRING-Datentyps ist die, dass man mit * die Länge des Textes
angeben kann. my_string_var braucht im Speicher 11 Byte, da noch ein 0-Byte dazu
kommt um den Text abzuschließen.

Dann gibt es noch eine Besonderheit. Die Variable my_byte_array wurde als Array defi-
niert. Damit wurden intern drei Speicherbereiche unter dem gleichen Namen reserviert.
Und man kann auf die einzelnen Speicherbereiche über den in Klammern geschriebenen
Index zugreifen (my_byte_array(1), my_byte_array(2), my_byte_array(3)). Das ist
von Vorteil, wenn man sich später Schreibarbeit sparen möchte und immer wiederkeh-
rende Aufgaben in einer Schleife abarbeiten möchte. Aber das ist jetzt noch nicht wichtig.

So schreibt man einen Wert in eine Variable:

my_bit_var = 1
my_byte_var = 200
my_byte_array(1) = 10
my_byte_array(2) = 20
my_byte_array(3) = 30
my_word_var = 20000
my_single_var = 10.5
my_string_var = "Hallo Welt"

Man kann einer Variable auch den Wert einer anderen Variable übergeben. Das sieht so
aus:

my_word_var = my_byte_var
my_byte_array(3) = my_byte_var
my_byte_array(2) = my_byte_array(1)

Man muss aber aufpassen! Man darf einer Variable keinen Wert übergeben, der nicht in
die Variable rein passt. Schreibt man den Wert einer WORD-Variable in eine BYTE-Vari-
able, dann werden nur die unteren 8 Bit übernommen. Die oberen 8 Bit werden einfach
abgeschnitten.

Dieser Code funktioniert:

my_word_var = 100
my_byte_var = my_word_var

Die Zahl 100, in einen Code aus Nullen und Einsen (Binärsystem) umgewandelt, sieht so
aus:

1100100

Das sind 7 einzelne Bits. Diese 7 Bits haben in einer BYTE-Variable Platz, da der Spei-
cherbereich für ein Byte 8 Bit groß ist.

Dieser Code funktioniert nicht:

my_word_var = 1000
my_byte_var = my_word_var
Die Dezimalzahl 1000, umgewandelt in eine Binärzahl (0/1), sieht so aus:

1111101000

Das sind 10 einzelne Bits. 10 Bits haben nicht mehr in einer BYTE-Variable platz. Es wer-
den die linken 2 Bit abgeschnitten. Im Speicher steht jetzt:

11101000

Wenn man diese Binärzahl in eine Dezimalzahl umwandelt, dann kommt die Dezimal-
zahl 232 dabei raus. In der Variable steht jetzt der Wert 232 und nicht wie erwartet der
Wert 1000.

Das Binärsystem und die Umrechnung von Binär nach Dezimal und umgekehrt erkläre ich
hier nicht. Das wird sowiso in vielen Computerbüchern erklärt und Wikipedia hat dazu
auch noch etwas zu sagen: http://de.wikipedia.org/wiki/Dualsystem

Umrechnung von Zahlensystemen: http://www.arndt-bruenner.de/mathe/scripts/Zahlen-


systeme.htm

Der Windows-Taschenrechner kann verschiedene Zahlensysteme umwanden. Man muss


ihn dafür nur in die wissenschaftliche Darstellung umschaltet.

Da wir ab jetzt in fast jedem weiteren Beispielprogramm mit Variablen arbeiten werden,
macht es früher oder später garantiert "Klick" und du wirst verstehen. ;-)

mfg Gerold :-)

PS: Für die Wissenshungrigen unter uns: http://avrhelp.mcselec.com/index.html?langu-


age_fundamentals.htm

Schalter an den Mikrocontroller anschliessen Teil 2

Es gibt noch mehr Möglichkeiten wie man auf einen Schalter reagieren kann. Hier ist die
nächste Möglichkeit: BITWAIT

BITWAIT ist ein Befehl der so lange an einer Stelle wartet, bis die übergebene Variable
oder ein Pin auf 0 oder auf 1 gesetzt wird. Dieser Befehl blockiert das gesamte Pro-
gramm. Dessen muss man sich bewusst sein.

Hier der zugehörige Schaltplan:


Und hier das Beispiel:

$regfile = "M8def.dat"
$crystal = 1000000
$hwstack = 100
$swstack = 100
$framesize = 100

'LED1 an PD7
Led1 Alias Portd.7
Config Led1 = Output

'LED2 an PD6
Led2 Alias Portd.6
Config Led2 = Output
'TASTER1 an PD2
Taster1 Alias Pind.2
Config Taster1 = Input
Portd.2 = 1 'PullUp-Widerstand einschalten

Dim Status As Bit

Do
'Warten bis der Taster1 gegen GND zieht
Bitwait Taster1 , Reset
'Warten bis der Taster1 gegen VCC zieht
Bitwait Taster1 , Set

'Wenn die Variable Status...


If Status = 0 Then
'...auf 0 gesetzt ist, dann LEDs wechseln...
Led1 = 0
Led2 = 1
'...und Status-Variable auf 1 setzen.
Status = 1
Else
'...auf 1 gesetzt ist, dann LEDs wechseln...
Led1 = 1
Led2 = 0
'...und Status-Variable auf 0 setzen.
Status = 0
End If
Loop

End

Wenn man den Taster drückt, dann wechseln die LEDs hin und her. Experimentiere damit
ein wenig. Lasse doch mal die Zeile Bitwait Taster1 , Set weg und schau was dann
passiert. Versuche es dir zu erklären.

Und eines dürfte bei diesem Beispiel ziemlich schnell auffallen. Wenn man den Taster
drückt, dann wird normalerweise die LED gewechselt. Aber ab und zu passiert nichts oder
es blinkt nur kurz auf.
Die meisten Taster oder Schalter "prellen". Das kommt daher, dass der Taster beim
Schalten ein wenig nachschwingt. Das Signal eines Tasters oder Schalters ist beim Schal-
ten nicht rein.

Das kann man auf der Hardwareseite umgehen, wenn man einen kleinen Keramikkon-
densator (sagen wir mal 10 nF bis 300 nF -- je nach Qualität des Schalters muss man
diesen Wert evt. ein wenig verändern) parallel zum Taster schaltet. Man kann dieses
Problem auch softwareseitig aus der Welt schaffen. Aber darauf möchte ich erst später
eingehen.

Hier der Schaltplan mit entprelltem Taster:

Vorsicht!
Wenn die Schaltung Strom bekommt, dann läd sich der Kondensator C3 über den inter-
nen PullUp-Widerstand auf. Das bedeutet, dass in diesem Moment PD2 nach GND gezo-
gen wird. Sobald der Kondensator aufgeladen ist, zieht der PullUp PD2 nach VCC. Da die
zwei Prüfungen mit BITWAIT so ziemlich das Erste sind was ausgeführt wird, wird die
Schleife einmal durchlaufen ohne einen Tastendruck abzuwarten. Möchte man das ver-
meiden, dann sollte man mit WAITMS ein paar Millisekunden warten bevor man die Main-
Loop loslegen lässt.
Übung 1:

$regfile = "M8def.dat"
$crystal = 1000000
$hwstack = 100
$swstack = 100
$framesize = 100

'LED1 an PD7
Led1 Alias Portd.7
Config Led1 = Output

'LED2 an PD6
Led2 Alias Portd.6
Config Led2 = Output

'LED2 an PD5
Led3 Alias Portd.5
Config Led3 = Output

'TASTER1 an PD2
Taster1 Alias Pind.2
Config Taster1 = Input
Portd.2 = 1 'PullUp-Widerstand einschalten

Dim Status As Byte

Do
Bitwait Taster1 , Reset
Bitwait Taster1 , Set

If Status = 0 Then
Led1 = 1
Led2 = 0
Led3 = 0
Status = 1
Elseif Status = 1 Then
Led1 = 0
Led2 = 1
Led3 = 0
Status = 2
Else
Led1 = 0
Led2 = 0
Led3 = 1
Status = 0
End If
Loop
End

Was macht dieses Programm?

Label, Unterprozeduren und Funktionen

Bevor ich weitere Möglichkeiten zeigen kann, wie man Signale an Eingängen erkennt,
muss ich noch auf die Strukturierung von Programmen eingehen, ohne die ich in weiteren
Beispielen nicht auskomme.

Es ist ziemlich schlechter Stil, einfach alle Befehle in einer Wurst untereinander zu schrei-
ben. So etwas nennt man unter Programmierern Spagetticode. Der Nachteil von Spa-
getticode ist, dass er nicht wartbar ist. Fehler lassen sich kaum finden und spätestens
nach ein paar Wochen ist man oft schneller wenn man ein Programm neu schreibt anstatt
dieses an geänderte Anforderungen anzupassen.

Natürlich ist man bei der Programmierung von µControllern sehr eingeschränkt und kann
nicht mit Klassen oder anderen Strukturierungsmöglichkeiten arbeiten, die in der Anwen-
dungsprogrammierung ganz normal sind.

Trotz der Speichereinschränkungen mancher µController, gibt es in Bascom Möglichkei-


ten, ein wenig Struktur in das Programm zu bringen. Es gibt so genannte "Label", die
man von beliebigen Codestellen aus anspringen kann, Unterprozeduren (Sub) und Funk-
tionen.

Ein Label ist eine "benannte" Stelle im Code. Diese Stelle kann mit dem Be-
fehl GOTO angesprungen werden. Das Programm springt dann zu dieser benannten
Stelle und arbeitet den Code ab dieser Stelle ab. GOTO ist die niederste Anweisung für so
etwas. Springt man mit GOTO ein Label an, dann gibt es keine dynamische Möglichkeit,
zur ursprünglichen Codezeile zurück zu kehren. Damit meine ich, dass sich der µC nicht
merkt, von wo aus zum Label gesprungen wurde um danach wieder zurück zu springen.
Dafür braucht GOTO keinen zusätzlichen Speicher um sich diese Information zu merken.

Basom Hilfe zu GOTO: http://avrhelp.mcselec.com/index.html?goto.htm

Dim A As Byte

Start:

A = A + 1
If A < 10 Then
Goto Start
End If

Print "Fertig"
End

GOTO ist ein alter und sehr umstrittener Befehl. GOTO gilt allgemein als das größte Prob-
lem von BASIC. Je öfter mit GOTO irgendwohin gesprungen wird, desto schwieriger wird
es dem Programmierer gemacht, dem Programmverlauf zu folgen. Meide GOTO wenn ein
Problem auch anders gelöst werden kann! Verwende GOTO wenn dadurch das Programm
einfacher wird (und das wird nur sehr selten der Fall sein).

Man kann ein Label auch mit dem Befehl GOSUB anspringen. GOSUB ist schon einen Le-
vel höher als GOTO. Bevor ein Label mit GOSUB angesprungen wird, wird im HWSTACK
(=Stapelspeicher) die Adresse des aktuell ausgeführten Befehls (=Programmzeiger) ab-
gespeichert. Diese Information wird benötigt, um nach einem RETURN wieder zu dieser
Stelle zurückkehren zu können. Der Befehl RETURN veranlasst die Rückkehr zur Stelle,
von der aus das Label mit GOSUB angesprungen wurde.

Du wirst dich vielleicht schon gewundert haben, was dieses $hwstack = 100 am Anfang
jedes Programmes soll. Damit wird die Größe dieses Stapelspeichers festgelegt. Je mehr
hin und her gesprungen wird, desto größer muss dieser Speicherbereich sein.

Basom Hilfe zu GOSUB: http://avrhelp.mcselec.com/index.html?gosub.htm

Dieses Beispiel geht davon aus, dass an PD5, PD6 und PD7 LEDs angeschlossen sind. Im
Beispiel sieht man recht gut, dass mit GOSUB zum Label gesprungen wird und mit RE-
TURN zum Aufrufpunkt zurückgekehrt wird.

$regfile = "M8def.dat"
$crystal = 1000000
$hwstack = 100
$swstack = 100
$framesize = 100

'LED1 an PD7
Led1 Alias Portd.7
Config Led1 = Output

'LED2 an PD6
Led2 Alias Portd.6
Config Led2 = Output

'LED3 an PD5
Led3 Alias Portd.5
Config Led3 = Output

Dim Status As Byte

Do
If Status = 0 Then
Gosub Set_led1
Status = 1
Elseif Status = 1 Then
Gosub Set_led2
Status = 2
Elseif Status = 2 Then
Gosub Set_led3
Status = 3
Elseif Status = 3 Then
Gosub Set_led3
Status = 4
Elseif Status = 4 Then
Gosub Set_led2
Status = 5
Else
Gosub Set_led1
Status = 0
End If

Waitms 200
Loop

End

Set_led1:
Led1 = 1
Led2 = 0
Led3 = 0
Return
Set_led2:
Led1 = 0
Led2 = 1
Led3 = 0
Return

Set_led3:
Led1 = 0
Led2 = 0
Led3 = 1
Return

An dieser Stelle möchte ich auch die Highlevel-Befehle "SUB" und "FUNCTION" anspre-
chen.

Je größer das Programm wird das man schreibt, desto wichtiger werden die Befehle SUB
und FUNCTION. Damit kann man abgeschlossene Programmbereiche (Unterprozeduren
und Funktionen) erstellen, die sogar ihre eigenen "lokalen" Variablen besitzen können.
Lokale Variablen sind außerhalb dieser Unterprozeduren und Funktionen nicht sichtbar
und können somit den Programmablauf nicht beeinflussen. Und genau das wird immer
wichtiger, je größer ein Programm wird. Je weniger ein Programmteil einen anderen Pro-
grammteil beeinflussen kann, desto weniger Fehler werden übersehen.

Eine Unterprozedur oder eine Funktion muss Bascom im Kopf des Programms mit DEC-
LARE bekannt gemacht werden. Eine Unterprozedur oder eine Funktion kann Parameter
übergeben bekommen. Eine Funktion kann zusätzlich auch noch Werte zurück geben.

$regfile = "M8def.dat"
$crystal = 1000000
$hwstack = 100
$swstack = 100
$framesize = 100

'LED1 an PD7
Led1 Alias Portd.7
Config Led1 = Output

'LED2 an PD6
Led2 Alias Portd.6
Config Led2 = Output

'LED2 an PD5
Led3 Alias Portd.5
Config Led3 = Output

'Prozedur deklarieren
Declare Sub Set_led(byval Led_id As Byte)

Dim Status As Byte

Do
If Status = 0 Then
Call Set_led(1)
Status = 1
Elseif Status = 1 Then
Call Set_led(2)
Status = 2
Elseif Status = 2 Then
Call Set_led(3)
Status = 3
Elseif Status = 3 Then
Call Set_led(3)
Status = 4
Elseif Status = 4 Then
Call Set_led(2)
Status = 5
Else
Call Set_led(1)
Status = 0
End If

Waitms 200
Loop

End
Sub Set_led(byval Led_id As Byte)
Led1 = 0
Led2 = 0
Led3 = 0

If Led_id = 1 Then
Led1 = 1
Elseif Led_id = 2 Then
Led2 = 1
Else
Led3 = 1
End If
End Sub

Links zur Bascom-Hilfe:

DECLARE SUB:
http://avrhelp.mcselec.com/index.html?declare_sub.htm
DECLARE FUNCTION:
http://avrhelp.mcselec.com/index.html?declare_function.htm
SUB:
http://avrhelp.mcselec.com/index.html?sub.htm
FUNCTION:
http://avrhelp.mcselec.com/index.html?declare_function.htm
CALL:
http://avrhelp.mcselec.com/index.html?call.htm
GOTO:
http://avrhelp.mcselec.com/index.html?goto.htm
GOSUB:
http://avrhelp.mcselec.com/index.html?gosub.htm

Interrupts

Du hat ja bereits BITWAIT kennen gelernt. Damit kann man auf einen Tastendruck war-
ten und daraufhin mit dem Programm fortfahren. Der Nachteil liegt auf der Hand Das
Programm bleibt stehen, bis die Taste gedrückt wurde. Genau deshalb ist es sehr selten,
dass man BITWAIT in Programmen antrifft.
Wenn das an einen Pin anliegende Signal nicht nachprellt, also z.B. von einem entprellten
Taster stammt, dann kann man das Signal vom µC beobachten lassen. Tritt das Signal
auf, dann unterbricht der µC die aktuelle Programmausführung und springt zu einem vor-
her bestimmten Label. Der Code unterhalb des Labels wird abgearbeitet und bei RETURN
springt der Programmzeiger wieder zurück zur vorher verlassenen Stelle.

Der Vorteil liegt auf der Hand. Das Programm läuft und läuft und läuft. Nur dann, wenn
sich auch wirklich etwas am beobachteten Pin tut, dann wird der dazupassene Code aus-
geführt. Das nennt man Interrupt oder Interrupt Request und das Label, welches bei so
einem Interrupt Request angesprungen wird, nennt man Interrupt Handler oder Interrupt
Request Handler. Im Mikrocontroller-Bereich hat sich auch die Bezeichnung "Interrupt
Service Routine" (ISR) eingebürgert.

So ein Interrupt unterbricht das laufende Programm. Setzt man Interrupts ein, muss man
das Programm so schreiben, dass es an jeder Stelle egal ist, wenn die Ausführung mal
kurz unterbrochen wird. -- Man kann sich aber auch absichern und Codeteile die nicht
durch einen Interrupt unterbrochen werden dürfen vor so einer Unterbrechung schützen.
Man kann mögliche Unterbrechungen einfach dadurch umgehen, indem man vor so ei-
nem Codeteil die Unterbrechung durch Interrupts ausschaltet und danach wieder ein-
schaltet. Der µC merkt sich die in der Zwischenzeit anfallenden Interrupts und arbeitet
sie danach ab.

Das Einfachste aber ist, wenn man in einem Interrupt-Handler nichts macht was lange
dauert. Idealerweise setzt man in einem Interrupt-Handler eine Variable und kümmert
sich um die restliche Abarbeitung des Jobs in der Hauptschleife.

Es gibt beim ATmega8 zwei Pins, die so einen Interrupt auslösen können. INT0 und INT1.
Wenn man sich den Anschlussplan im ATmega8-Datenblatt auf Seite 2 ansieht, dann fin-
det man heraus, dass INT0 der Pin PD2 und INT1 der Pin PD3 ist. Dieses Wissen können
wir jetzt dafür nutzen, um eine LED bei einem Tastendruck ein und bei erneutem Tasten-
druck auszuschalten. Und um zu beweisen, dass die Hauptschleife währenddessen weiter
läuft, lassen wir eine zweite LED in der MainLoop blinken.

Zum Testen genügt diese Schaltung


Ein neuer Befehl wird eingeführt: TOGGLE

TOGGLE entspricht (in etwa) dieser Befehlsfolge:

IF bit_variable = 0 THEN
bit_variable = 1
ELSE
bit_variable = 0
END

Die neuen Befehle sind im Code beschrieben.

$regfile = M8def.dat
$crystal = 1000000
$hwstack = 100
$swstack = 100
$framesize = 100
'LED1 an PD7
Led1 Alias Portd.7
Config Led1 = Output

'LED2 an PD6
Led2 Alias Portd.6
Config Led2 = Output

'TASTER1 an PD2
Taster1 Alias Pind.2
Config Taster1 = Input
Taster1 = 1 'PullUp-Widerstand einschalten

'Wenn der Interrupt INT0 auftritt, dann springe zum Label On_int0
On Int0 On_int0

'Der Interrupt INT0 wird ausgelöst wenn der Pin PD2 gegen GND gezogen
wird.
'Also beim Übergang von HIGH nach LOW. Der Interrupt wird nicht ausge-
löst, wenn
'der Pin bereits gegen GND zieht.
Config Int0 = Falling 'fallende Flanke

'INT0 einschalten
Enable Int0

'Interrupts global einschalten. Das ist der Hauptschalter für alle In-
terrupts
Enable Interrupts

Do
Toggle Led1
Waitms 200
Loop

End
On_int0:
Toggle Led2
Return

AVR im KFZ-Boardnetz

Wie ist das denn so in einem Auto? Dort hat man 12 V (13,8 V) aber keine 5 Volt, die
man an einen Pin des ATmega8 legen kann. Also muss ein Spannungsteiler her. Und da-
mit evt. auftretende Spannungsspitzen dem µC nichts anhaben können, würde ich noch
eine schnelle Z-Diode an die Leitung legen.

Version 1:

Version 2:

Bessere Schaltungsvorschläge sind ausdrücklich erwünscht.

Diese beiden Schaltungen sind nicht wirklich gut. Wenn ich mal wieder etwas Zeit habe,
werde ich mich an einer besseren Eingangsbeschaltung probieren. Hauptsache ist jetzt
mal, dass ich dich darauf aufmerksam machen konnte, dass das KFZ-Boardnetz nicht so
stabil ist wie man glauben könnte. Die Spannungen die dort auftreten können, gehen von
-150 V bis + 150 V. Und deine Schaltung muss davor geschützt werden. Sonst ist sie hin.

Der AVR muss viel besser als normal geschützt werden. Die Stromversorgung in einem
KFZ-Boardnetz könnte so aussehen:
Das sollte man sich zu diesem Thema unbedingt durchlesen: http://www.dse-faq.elektro-
nik-kompendium.de/dse-faq.htm#F.23

Taster softwareseitig entprellen

In diesem Beitrag möchte ich auf die Möglichkeit eingehen, einen Taster softwareseitig zu
entprellen. Also als Ersatz für den, in früheren Beiträgen, eingeführten Keramikkondensa-
tor, den man parallel zum Schalter löten kann.

Der Kondensator ist eine feine Sache und oft gibt es auch keine andere Möglichkeit um
zu einem sauberen Signal zu kommen. Aber manchmal möchte man Bauteile sparen oder
man hat zu wenig Platz für die zusätzlichen Kondensatoren oder man hat einfach keine
Lust, sich einen Kondensator raus zu suchen. ;-) Für diese Fälle gibt es eine in Bascom
eingebaute High-Level-Prozedur: DEBOUNCE

Bascom Hilfe zu DEBOUNCE: http://avrhelp.mcselec.com/index.html?debounce.htm

An DEBOUNCE übergibt man als Parameter den Pin an dem der Taster hängt. Dann kann
man noch angeben, ob bei einer HIGH-LOW-Flanke (0) oder bei einer LOW-HIGH-Flanke
(1) die Taste als gedrückt gilt. Als dritten Parameter gibt man an, wohin das Programm
springen soll, wenn ein Tastendruck erkannt wurde. Und als vierten Parameter kann man
mit dem Wort "SUB" angeben ob für den Sprung zum Label der Befehl GOTO oder GO-
SUB verwendet werden soll. Lässt man "SUB" weg, dann wird GOTO verwendet. Fügt
man "SUB" an, dann wird GOSUB verwendet. Bei Verwendung von GOSUB wird das Pro-
gramm bei einem RETURN wieder an der ursprünglichen Position fortgesetzt.

DEBOUCE ist ein Befehl den man in einer Schleife benutzt. Es wird also nicht wie beim
Interrupt alles der Hardware überlassen. DEBOUNCE merkt sich im Hintergrund den aktu-
ellen Status des zu überwachenden Pins und wird aktiv wenn sich das Signal geändert
hat. Ändert sich das Signal nicht, dann wird ganz normal mit dem nächsten Befehl fortge-
fahren. Hat sich das Signal geändert, dann wartet DEBOUNCE 25 Millisekunden und prüft
dann den Pin noch einmal ab. Ist der Pin nach 25 Millisekunden immer noch im Schaltzu-
stand, dann springt DEBOUNCE zum angegebenen Label. Mit CONFIG DEBOUNCE kann
man diese Wartezeit einstellen.

$regfile = "M8def.dat"
$crystal = 1000000
$hwstack = 100
$swstack = 100
$framesize = 100

'LED1 an PD7
Led1 Alias Portd.7
Config Led1 = Output

'LED2 an PD6
Led2 Alias Portd.6
Config Led2 = Output

'LED3 an PD5
Led3 Alias Portd.5
Config Led3 = Output

'TASTER1 an PD2
Taster1 Alias Pind.2
Config Taster1 = Input
Portd.2 = 1 'PullUp-Widerstand einschalten

Do
Debounce Taster1 , 0 , On_taster1 , Sub
Loop

End

On_taster1:
If Led1 = 0 Then
Led1 = 1
Led2 = 0
Else
Led1 = 0
Led2 = 1
End If
Return

DEBOUNCE hat auch seine Nachteile, die ich hier nicht verschweigen möchte. Am Ein-
fachsten ist es, wenn man zum Demonstrieren ein Beispiel heran zieht.

Tausche den Code der MainLoop

Do
Debounce Taster1 , 0 , On_taster1 , Sub
Loop

durch diesen Code aus

Do
Debounce Taster1 , 0 , On_taster1 , Sub
Toggle Led3
Loop

Damit wird LED3 in der MainLoop ständig ein- und ausgeschaltet. Das geht so schnell,
dass das Auge das nicht mehr mitbekommt. Für uns leuchtet die LED ständig. Wenn man
jetzt den Taster mehrmals hintereinander drückt, dann flackert die LED3 jedes mal ein
wenig. Das kommt daher, weil beim Erkennen eines Tastendrucks von DEBOUNCE 25 ms
gewartet wird. Wärend dieser 25 ms wird die LED nicht mehr getoggelt und das ist dieses
Flackern. Das Programm wird bei Tastendruck für 25 ms angehalten.

Tausche den Code der MainLoop jetzt durch diesen Code aus:

Do
Debounce Taster1 , 0 , On_taster1 , Sub
Waitms 400
Toggle Led3
Loop

Was passiert hier? LED3 blinkt. Zwischen jedem Toggeln der LED3 werden im Programm
400 Millisekunden gewartet. Während dieser 400 Millisekunden hat DEBOUNCE keine
Chance einen Tastendruck zu ermitteln, da es schlicht und einfach viel zu wenig oft aus-
geführt wird um eine Änderung zu erkennen. Ganz langsame Tastenanschläge werden er-
kannt. Aber je schneller man die Taste drückt desto mehr Anschläge gehen verloren.

Wird es als Störung empfunden wenn DEBOUNCE das Programm 25 ms lahm legt, oder
ist der µC sehr ausgelastet und wird ständig am Limit betrieben, oder wird öfter im Pro-
gramm mit WAIT oder WAITMS gewartet, dann ist DEBOUNCE nicht geeignet um einen
Tastendruck zu erkennen. In solchen Fällen sollte man auf einen der beiden Interrupts
(INT0, INT1) oder eine andere Möglichkeit ausweichen.
Fuse-Bits und Lock-Bits - Teil 1

Jetzt möchte ich ein Thema erklären, welches von vielen ungern angesprochen wird. Aber
so undurchsichtig ist es eigentlich gar nicht -- wenn man die richtigen Werkzeuge zur
Hand hat oder ein wenig im Datenblatt des jeweiligen µC stöbert. Ich spreche von den
Fuse-Bits und den Lock-Bits.

Die Fuse-Bits und Lock-Bits sind die Grundeinstellungen des AVR-Mikrocontrollers. Diese
Grundeinstellungen werden nicht beim normalen Übertragen eines Programmes geändert
oder gelöscht. Unter anderem wird in diesen Grundeinstellungen festgelegt, ob der µC
mit dem internen RC-Oszillator oder mit einem externen Quarz betrieben wird.

Der ATmega8 hat einen RC-Oscillator eingebaut. Dieser liefert in der Basiseinstellung ei-
nen Takt von 1 Mhz. Diese Basiseinstellung kann man verändern. Z.B. so, dass der in-
terne RC-Oszillator mit 8 Mhz und nicht mehr mit 1 Mhz läuft. Das ist noch nicht
schlimm. Aber damit kann man auch einstellen, dass der Takt von einem externen Quarz
oder Oszillator kommt. Hat man das so eingestellt und schließt keinen Quarz an, dann
läuft der µC nicht bis ein Quarz angeschlossen wird.

Die Fuse- und Lock-Bits können mit einem Programmer verändert werden. Ist der Pro-
grammer ein ISP-Programmer (In System Programming), dann kann man alle Einstellun-
gen, bis auf die Einstellung die dafür zuständig ist, dass man den µC per ISP program-
mieren kann, verändern.

Die Fuse-Bits für den Systemtakt werden sehr detailliert im Kapitel "System Clock and
Clock Options" im detaillierten ATmega8-Datenblatt erklärt. Dann gibt es noch Einstellun-
gen, die den Brown Out Detektor, den Watchdog Timer und den Boot Loader betreffen.
Das haben wir noch nicht besprochen, deshalb gehe ich jetzt nicht näher darauf ein. Es
gibt da noch eine Einstellung mit der man den Reset-Pin in einen normalen I/O-Pin um-
schalten kann. Wenn man das macht, dann kann der µC nicht mehr über ISP program-
miert werden. Also Finger weg von der Einstellung mit dem Namen "RSTDISBL". Das
kann man nur wieder umstellen, wenn man den µC mit einem speziellen Programmer
(z.B. dem AVR-Dragon) programmiert.

Dann gibt es noch die Lock-Bits. Mit diesen Einstellungen kann man z.B. den Programm-
code vor dem Auslesen schützen. Der eingebaute EEPROM-Speicher kann auch geschützt
werden. Die Lock-Bits werden im detaillierten ATmega8-Datenblatt im Kapitel "Program
And Data Memory Lock Bits" aufgelistet.

Das Verwirrende bei den Fuse- und Lock-Bits ist, dass eine 1 bedeutet, dass die Einstel-
lung NICHT] gesetzt ist und eine 0 bedeutet, dass die Einstellung GESETZT ist. Es ist
also genau umgekehrt als das was wir gewohnt sind.

Bei den Lock-Bits kann man nichts falsch machen wenn man sie einfach unprogrammiert
(auf 1 gesetzt) lässt. Deshalb werde ich nicht weiter darauf eingehen. Wer mehr darüber
wissen will, sollte sich im ATmega8-Datenblatt darüber informieren.

Die Lock- und Fuse-Bits werden im detaillierten Datenblatt des ATmega8 im Kapitel "Me-
mory Programming" genau erklärt.

Die Fuse-Bits befinden sich in zwei Registern. Es handelt sich also um 16 Einstellungen
(einzelne Bits) die auf 0 oder 1 gesetzt werden können. Diese zwei Register werden in
den verschiedenen Programmen oft "High Byte" und "Low Byte" genannt. Diese werden
in den Programmen, die man zum Einstellen der Fuse-Bits verwenden kann, auch manch-
mal einzeln angezeigt.

Im Fuse High Byte des ATmega8 befinden sich diese Einstellungen:

Im Fuse Low Byte des ATmega8 befinden sich diese Einstellungen:

Wichtig
Wenn man nicht weiß, wie ein Bit eingestellt werden soll, dann sollte man die in diesen
Tabellen aufgezeigte Standardeinstellung verwenden.

Die gleichnamigen Bits, wie z.B. SUT1 und SUT0 bilden zusammen eine Einstellung. Und
wenn man wissen will was eine Einstellung genau bewirkt, dann sollte man das AT-
mega8-Datenblatt nach dieser Einstellung durchsuchen. Das funktioniert mit allen gängi-
gen PDF-Reader wie z.B. dem "Foxit Reader" oder dem "Adobe Reader".

Im nächsten Beitrag erkläre ich, wie man mit dem mySmartUSB die Fuse-Bits einstellen
kann.

Fuse-Bits und Lock-Bits - Teil 2

Zum mySmartUSB gehört auch das Programm "myAVR ProgTool Version 1.01". Damit kann man die Fuse-
Bits einstellen. Aber das ist derzeit so schlecht und unpraktisch gelöst, dass es quasi nicht benutzbar ist. Man hat
mir zugesagt, dass es in ein paar Wochen eine neue Version dieses Tools geben soll. Bis dahin empfehle ich
nicht dieses Tool, sondern die Demo Version des Programmes "myAVR WorkpadPLUS" zum Einstellen der
Fuse-Bits zu verwenden.

Zuerst holt man sich die aktuelle Demo Version die man unter der Adresse http://myavr.de/download.php herun-
terladen kann. Suche dort einfach nach "myAVR Workpad PLUS". Während ich diesen Beitrag schreibe ist dort
die "myAVR Workpad PLUS, Demo Version 1.4" aktuell. Nach dem Herunterladen kann das Programm wie
üblich installiert werden.

Dann sollte man den mySmartUSB einstecken und an diesen einen ATmega8 anschließen. Z.B. über eines der
empfohlenen Experimentierboards oder über ein Steckbrett. Dann kann man das Programm starten.

Über das Menü "Extras/Einstellungen" kommt man in das Fenster "Optionen - myAVR Workpad PLUS". In die-
sem kann man nun den "mySmartUSB" auswählen und testen. Links unten kann man noch den angeschlossenen
Controller auswählen. Das sollte der "ATmega8" sein. Mit einem Klick auf die Schaltfläche "Speichern", werden
die Einstellungen gespeichert und kehrt zum Hauptfenster zurück.

Zu den Fuse-Bits kommt man über das Menü "Extras/Fuse- und Lock-Bits". Wenn der µC erkannt wird, dann
sollte das Fenster in etwa so aussehen:
Einstellungen
Brown-Out

Mit einem Häckchen bei "Brown-out detection enabled" kann man den Brown-Out-Detektor einschalten. Dieser
schaltet den µC in den Reset-Modus, solange die hier angegebene Spannung unterschritten wird. Wenn das Pro-
gramm nur dann korrekt funktioniert wenn die Versorgungsspannung passt, dann ist die Brown-Out-Erkennung
eine feine Sache. Z.B. braucht das Schreiben von Daten in den EEPROM eine Mindestspannung und würde nicht
funktionieren wenn diese unterschritten wird. Es könnte z.B. sein, dass sich die Spannung beim Einschalten des
Gerätes langsam aufbaut weil vielleicht ein großer Kondensator geladen werden muss. In so einem Fall kann
man Brown-Out aktivieren, um das Programm erst dann starten zu lassen, wenn die benötigte Spannung am µC
anliegt.

Ext-Clock

Mit diesen drei Einstellungen kann man den µC so einstellen, dass dieser seinen Takt von einem externen Takt-
geber erhält. Diese Einstellung sagt dem µC, dass ein eigenständiges Taktsignal von einem Oszillator kommt --
bitte nicht mit einem Quarz verwechseln. Für einen Quarz gibt es andere Einstellungen.

Setzt man zusätzlich auf der nächsten Seite bei der Einstellung CKOPT ein Häckchen, dann wird ein eingebauter
36 pF Kondensator zwischen XTAL1 und GND aktiviert. Das Häckchen bei CKOPT sollte bei diesen drei Ein-
stellungen normalerweise nicht gesetzt sein.

Detaillierte Informationen darüber findet man im ATmega8-Datenblatt im Kapitel "External Clock".

Die Angaben "6 CK + 0 ms", "6 CK + 4 ms" und "6 CK + 64 ms" geben an, wie lange der µC dem Oszillator
Zeit geben soll um sich einzuschwingen. Viele Oscillatoren brauchen ein wenig Zeit bevor diese im richtigen
Takt schwingen. Damit in dieser Zeit keine zeitkritischen Dinge vom µC erledigt werden, wartet der µC die an-
gegebene Zeit ab bevor das Programm gestartet wird. Wenn es nicht wichtig ist, dass der µC sehr schnell nach
dem Anlegen der Spannung verfügbar ist, dann sollte man dem Oszillator die maximale Zeit zum Einschwingen
geben. Wenn es wichtig ist, dass der µC so schnell wie möglich verfügbar ist, dann sollte man zuerst das Kapitel
darüber im Datenblatt lesen bevor man die Einschwingzeit verkürzt. Die hiermit eingestellte Einschwingzeit gilt
auch für das Aufwachen des µC, wenn man diesen vorher in einen Schlafmodus gesetzt hat. Dieser Absatz gilt
für alle weiteren, hier aufgeführten Clock-Einstellungen und wird für die anderen Einstellungen nicht mehr ge-
sondert erklärt.

Int. RC Osc.

Mit diesen Einstellungen stellt man den µC so ein, dass dieser ohne externen Oscillator oder Quarz funktioniert.
Es wird der in den µC eingebaute RC-Oscillator verwendet. Dieser eingebaute RC-Oscillator kann auf 1, 2, 4
und 8 Mhz eingestellt werden. Die Standardeinstellung ist 1 Mhz. Das ist nicht sonderlich schnell, braucht aber
weniger Strom als eine hohe Frequenz. Je höher die Frequenz, desto öfter wird das Programm durchlaufen.
Desto öfter werden Messungen vorgenommen und Pins auf HIGH oder LOW gesetzt. Die Frequenz kann bis zu
3% (1% wenn kalibriert) von der Vorgabe abweichen. Der eingebaute RC-Oscillator kann nie so genau wie ein
Quarz sein. Aber für viele, nein sogar für die allermeisten, Anwendungen wird der eingebaute RC-Oscillator ge-
nügen. Wählt man eine dieser Einstellungen, dann muss auf der nächsten Seite das Häckchen von der Einstel-
lung CKOPT entfernt werden, damit nicht noch zusätzlich die eingebauten 36 pF Kondensatoren aktiviert wer-
den.

Da ich in den nächsten Beiträgen vermehrt auf die Verbindung zwischen µC und Computer über RS-232 einge-
hen werde, ist es interessant, hier auf 8 Mhz zu stellen. Warum das für die Kommunikation mit dem Computer
besser als 1 Mhz ist werden ich später erklären.

Ext. RC Osc.
Man kann mit einem Widerstand (R) und einem Kondensator (C) einen eigenen RC-Oscillator aufbauen. Je nach
Größe des Widerstands und Kapazität des Kondensators schwingt dieser RC-Oscillator in verschieden hohen
Frequenzen. Um einen externen RC-Oscillator als Taktgeber zu verwenden, nimmt man diese Einstellungen. Es
muss nur noch angegeben werden, wie hoch in etwa die Frequenz des RC-Oscillators ist, damit sich der µC in-
tern besser auf die Frequenz einstellen kann. Setzt man auf der nächsten Seite ein Häckchen bei CKOPT, dann
wird zwischen XTAL1 und GND ein 36 pF Kondensator aktiviert. So kann man den externen RC-Oscillator mit
nur einem Widerstand aufbauen. Ein Schaltplan dafür ist im ATmega8-Datenblatt zu finden.

Ext. Low-Freq. Crystal

Diese Einstellung nimmt man, wenn man einen 32.768 Hz Uhrenquarz anschließt. Setzt man auf der nächsten
Seite das Häckchen bei der Einstellung CKOPT, dann kann man sich die externen Kondensatoren sparen. Mit
CKOPT werden die internen 36 pF Kondensatoren an den Pins XTAL1 und XTAL2 aktiviert.

Ext. Cristal/Resonator

Damit kann man an die Pins XTAL1 und XTAL2 einen Quarz anschließen. Beide Pins des
Quarz müssen mit jeweils einem 22 pF Keramikkondensator gegen GND geschaltet wer-
den.
Damit ist es möglich, den µC sehr genau zu takten. Das ist für die verschiedensten An-
wendungen wichtig. Z.B. wenn man eine Frequenz messen oder eine genaue Frequenz
erzeugen möchte. Oder wenn mit hoher Geschwindigkeit mit dem Computer oder mit an-
deren µControllern kommuniziert wird. Der interne RC-Oscillator kann maximal 8 Mhz er-
zeugen. Mit einem externen Quarz kann der µC mit bis zu 16 Mhz betrieben werden.

Low Freq.:

 0,4 bis 0,9 Mhz


 nur Keramik-Oszillator

Medium Freq.:

 0,9 bis 3 Mhz

High Freq.:

 3-8 Mhz
 mit einem Häckchen bei CKOPT bis zu 16 Mhz

Um einen Quarz mit einer Frequenz zwischen 8 und 16 Mhz anschließen zu können, muss
ein Häckchen bei der Einstellung CKOPT (auf der nächsten Seite) gesetzt werden.

Vorsicht!
Nachdem die Fuse-Bits umgestellt wurden, muss der µC kurz von der Versorgungsspan-
nung getrennt werden. Erst dann werden die neuen Einstellungen wirksam.

Fuse-Bits und Lock-Bits - Teil 3

Das Low-Byte der Fuses wurde im letzten Beitrag erklärt. Jetzt ist das High-Byte dran.
Einstellungen
Reset Disabled

Damit wird aus dem Reset-Pin (PC6) ein normaler I/O-Pin. Man hat damit zwar einen Pin mehr zur Verfügung,
kann den µC aber nicht mehr über ISP programmieren.

Watch-dog Timer always on

Über den Watch-dog Timer haben wir noch nicht gesprochen. Aber vorab: Damit prüft man in gewissen Zeitab-
ständen ob der µC noch reagiert. Wenn nicht, dann wird ein Reset ausgelöst. Dazu muss man im Programm sel-
ber immer wieder den Timer zurück setzen. Der Watch-dog Timer funktioniert auch ohne diese Einstellung. Und
ein Programm das nicht ständig den Watch-dog Timer zurück setzt, läuft mit dieser Einstellung sowiso nicht.
Also -- immer ausgeschaltet lassen.

Serial program downloading (SPI) enabled

Ist diese Einstellung gesetzt, dann kann der µC per SPI programmiert werden.

Preserve EEPROM memory through the Chip Erase cycle

Mit den Programmen, mit denen man ein Programm vom Computer zum µC überträgt, kann man auch den ge-
samten µC löschen. Um zu verhindern, dass damit auch der EEPROM des µC gelöscht wird, kann man diese
Einstellung setzen.

Boot Flash section size

Das ist für uns nicht wichtig. (Wenn es dich interessiert: siehe Datenblatt)

Boot Reset vector Enabled

Das ist für uns nicht wichtig. (Wenn es dich interessiert: siehe Datenblatt)
CKOPT

Diese Einstellung wurde im vorherigen Beitrag erklärt. Damit kann man, je nach CKSEL-
Einstellung einen oder zwei eingebaute Kondensatoren für den Oszillator aktivieren. Für
Frequenzen ab 8 Mhz muss diese Einstellung gesetzt werden.

Weiterführende Links:

 http://www.wiki.elektronik-projekt.de/mikrocontroller/avr/fusebit_tutorial

Fusebits Standardeinstellungen

Es geht um die Frage, wie die Fusebits in den meisten Fällen eingestellt werden müssen.

Und hier ist sie, die Auflistung der meist eingesetzten Standardeinstellungen der Fusebits
für den ATmega8.

mySmartUSB mit "myAVR Workpad Plus"


1 Mhz interner RC-Oscillator
Low Fuse
 11100001

 Brown-out detection level at VCC=2.7 V; [BODLEVEL=1]


 Int. RC Osc. 1 MHz; Start-up time: 6 CK + 64 ms; [CHSEL=0001 SUT=10]; default
value
High Fuse
 11011001

 Serial program downloading (SPI) enabled; [SPIEN=0]


 Boot Flash section size=1024 words Boot start address=$0C00; [BOOTSZ=00];
default value
Lockbits
 11111111

 Mode 1: No memory lock features enabled


 Application Protection Mode 1: No Lock on SPM and LPM in Application Section
 Boot Loader Protection Mode 1: No lock on SPM and LPM in Boot Loader Section
8 Mhz interner RC-Oscillator
Low Fuse
 11100100

 Brown-out detection level at VCC=2.7 V; [BODLEVEL=1]


 Int. RC Osc. 8 MHz; Start-up time: 6 CK + 64 ms; [CKSEL=0100 SUT=10]
High Fuse
 11011001

 Serial program downloading (SPI) enabled; [SPIEN=0]


 Boot Flash section size=1024 words Boot start address=$0C00; [BOOTSZ=00];
default value
Lockbits
 11111111

 Mode 1: No memory lock features enabled


 Application Protection Mode 1: No Lock on SPM and LPM in Application Section
 Boot Loader Protection Mode 1: No lock on SPM and LPM in Boot Loader Section
3-8 Mhz externer Quarz
Low Fuse
 11111111

 Brown-out detection level at VCC=2.7 V; [BODLEVEL=1]


 Ext. Crystal/Resonator High Freq.; Start-up time: 16K CK + 64 ms; [CKSEL=1111
SUT=11]
High Fuse
 11011001

 Serial program downloading (SPI) enabled; [SPIEN=0]


 Boot Flash section size=1024 words Boot start address=$0C00; [BOOTSZ=00];
default value
Lockbits
 11111111

 Mode 1: No memory lock features enabled


 Application Protection Mode 1: No Lock on SPM and LPM in Application Section
 Boot Loader Protection Mode 1: No lock on SPM and LPM in Boot Loader Section
8-16 Mhz externer Quarz
Low Fuse
 11111111

 Brown-out detection level at VCC=2.7 V; [BODLEVEL=1]


 Ext. Crystal/Resonator High Freq.; Start-up time: 16K CK + 64 ms; [CKSEL=1111
SUT=11]
High Fuse
 11001001

 Serial program downloading (SPI) enabled; [SPIEN=0]


 Boot Flash section size=1024 words Boot start address=$0C00; [BOOTSZ=00];
default value
 CKOPT fuse (operation dependent of CKSEL fuses); [CKOPT=0]
Lockbits
 11111111

 Mode 1: No memory lock features enabled


 Application Protection Mode 1: No Lock on SPM and LPM in Application Section
 Boot Loader Protection Mode 1: No lock on SPM and LPM in Boot Loader Section
PonyProg
1 Mhz interner RC-Oscillator

Lockbits
 11111111

High Fuse
 11011001

 SPIEN
 BOOTSZ1
 BOOTSZ0
Low Fuse
 11100001

 SUT0
 CKSEL3
 CKSEL2
 CHSEL1
8 Mhz interner RC-Oscillator

Lockbits
 11111111
High Fuse
 11011001

 SPIEN
 BOOTSZ1
 BOOTSZ0
Low Fuse
 11100100

 SUT0
 CKSEL3
 CKSEL1
 CHSEL0
3-8 Mhz externer Quarz

Lockbits
 11111111

High Fuse
 11011001

 SPIEN
 BOOTSZ1
 BOOTSZ0
Low Fuse
 11111111
8-16 Mhz externer Quarz

Lockbits
 11111111

High Fuse
 11001001

 SPIEN
 CKOPT
 BOOTSZ1
 BOOTSZ0
Low Fuse
 11111111

UART - RS-232 vom Mikrocontroller zum Computer

Ich habe an diesem Beitrag nicht durchgehend gearbeitet. Deshalb kann es sein, dass er evt. ein wenig auseinan-
dergerissen wirkt. Hoffentlich bringe ich kein zu großes Kuddelmuddel rein. :-)

14.400 * 2 = 28.800
28.800 * 2 = 57.600
57.600 * 2 = 115.200
115.200 * 2 = 230.400
230.400 * 2 = 460.800
460.800 * 2 = 921.600
921.600 * 2 = 1.843.200
1.843.200 * 2 = 3.686.400
3.686.400 * 2 = 7.372.800
7.372.800 * 2 = 14.745.600

Du wirst dir jetzt vielleicht denken, dass der Gerold ausgeflippt ist und nicht mehr weiß wie er diesen Beitrag
mit Content füllen soll. :-) Aber vielleicht kommen dir die fett geschriebenen Zahlen an der linken Seite bekannt
vor. Es sind mögliche Geschwindigkeiten für COM-Verbindungen (auch RS-232 oder "Serielle Schnittstelle"
genannt).
Auf der rechten Seite stehen, fett markiert, die möglichen Quarz-Frequenzen die idealerweise dafür verwendet
werden, um genau diese Baudraten zu erreichen. Einzig durch das Teilen der Frequenz kommt man auf eine die-
ser Baudraten.

Die Geschwindigkeit von COM-Verbindungen gibt man in Bit/Sekunde oder Baud an. 14.400 Baud bedeutet,
dass 14.400 Bit/Sekunden über die Leitung übertragen werden. Bei den üblichen Standardeinstellungen braucht
man zum Übertragen von 8 Bit (1 Byte) genau 10 Bit. Es kommt ein Start-Bit und ein Stop-Bit dazu. Bei einer
Geschwindigkeit von 14.400 Baud werden also 1.440 Byte die Sekunde über die Leitung gejagt. Bei einer Baud-
rate von 115.200 werden 11.520 Byte die Sekunde übermittelt.

So, jetzt habe ich genug wirres Zeug geschrieben. ;-) Es geht in diesem Beitrag darum, wie man Daten vom µC
zum Computer übertragen kann. Als einfachste Möglichkeit steht auf dem Computer die RS-232-Schnittstelle
zur Verfügung. Und viele der AVR-µController haben UART und USART eingebaut. UART steht für "Univer-
sal Asynchronous Receiver Transmitter" und USART steht für "Universal Synchronous/Asynchronous Receiver
Transmitter". Das ist genau das was wir brauchen um uns über die RS-232-Schnittstelle mit dem Computer zu
unterhalten. Siehe Wikipedia: http://de.wikipedia.org/wiki/UART

Verpackung der Daten

Ein Datenbyte wird verpackt: Zuerst kommt ein Startbit. Dann die Datenbits und optional ein Bit für die Parität
(sehr selten) mit dazu. Zum Abschluss kommt noch ein Stopbit dran. So, oder ein wenig abgeändert, werden die
Datenbits über die Leitung verschickt. Am anderen Ende werden die Datenbits wieder ausgepackt. Damit sich
beide Gegenstellen verstehen, müssen sie sich an die ausgemachte Geschwindigkeit halten. Es nützt nichts, wenn
eine Seite die Daten mit 115.200 Baud über die Leitung schickt wenn die andere Seite die Daten mit der Baud-
rate 4.800 erwartet. Da der Takt nicht über eine zusätzliche Leitung geschickt wird, bleiben beiden Seiten nur die
Quarze um die Geschwindigkeit der Datenübertragung genau einzuhalten.

Der USART ist im ATmega8-Datenblatt das Kapitel "USART" gewidmet. Dort findet man genaue Angaben zu
den möglichen Frame-Formaten (die Verpackung der Daten) und natürlich auch die optimalen Frequenzen für
den µC.
Frequenzen und damit machbare Baudraten

Die Baudrate sollte sich aus der Quarz-Frequenz durch einfaches Teilen ermitteln lassen und wenn das nicht
möglich ist, höchstens 2% von dieser abweichen.

Die idealen Frequenzen stehen im ATmega8-Datenblatt im Kapitel "Examples of Baud Rate Setting".

1 Mhz:
0,2% Abweichung: 2.400, 4.800
1,8432 Mhz:
fehlerfrei: 2.400, 4.800, 9.600, 14.400, 19.200, 28.800, 38.400, 57.600, 76.800, 115.200,
230.400 Baud
2 Mhz:
0,2% Abweichung: 2.400, 4.800, 9.600, 19.200 Baud
fehlerfrei: 250.000 Baud
3,6864 Mhz:
fehlerfrei: 2.400, 4.800, 9.600, 14.400, 19.200, 28.800, 38.400, 57.600, 76.800, 115.200,
230.400 Baud
4 Mhz:
0,2% Abweichung: 2.400, 4.800, 9.600, 38.400 Baud
fehlerfrei: 250.000, 500.000 Baud
7,3728 Mhz:
fehlerfrei: 2.400, 4.800, 9.600, 14.400, 19.200, 28.800, 38.400, 57.600, 76.800, 115.200,
230.400 Baud
8 Mhz:
0,2% Abweichung: 2.400, 4.800, 9.600, 19.200, 38.400, 76.800 Baud
fehlerfrei: 250.000, 500.000, 1.000.000 Baud
11,0592 Mhz:
fehlerfrei: 2.400, 4.800, 9.600, 14.400, 19.200, 28.800, 38.400, 57.600, 76.800, 115.200,
230.400 Baud
14,7456 Mhz:
fehlerfrei: 2.400, 4.800, 9.600, 14.400, 19.200, 28.800, 38.400, 57.600, 76.800, 115.200,
230.400 Baud
16 Mhz:
0,1% bis 0,2% Abweichung: 4.800, 9.600, 14.400, 19.200, 38.400, 76.800 Baud
fehlerfrei: 2.400, 250.000, 500.000, 1.000.000 Baud
18,432 Mhz:
fehlerfrei: 2.400, 4.800, 9.600, 14.400, 19.200, 28.800, 38.400, 57.600, 76.800, 115.200,
230.400 Baud
20 Mhz:
-0,2% bis 0,2% Abweichung: 9.600, 14.400, 19.200, 28.800, 38.400 Baud
fehlerfrei: 2.400, 4.800, 250.000, 500.000 Baud
Wie man sieht, ist 4.800 Baud überall mit dabei. Auch bei 1 Mhz, der Grundeinstellung des ATmega8. Deshalb
verwende ich für zeitunkritische Datenübertragungen gerne 4.800 Baud. Erst wenn ich mehr Geschwindigkeit
brauche (was sehr selten ist) kümmere ich mich um den richtigen Quarz.

Spannung

Also, jetzt weißt du schon mal, dass die Geschwindigkeit für die Datenübertragung sehr wichtig ist. Jetzt kommt
noch ein Fallstrick mit dazu -- die Spannung. Die UART des ATmega8 arbeitet mit der für diesen µC üblichen
Spannung. Das ist im Normalfall 5 Volt. Ein LOW entspricht 0 Volt und ein HIGH entspricht 5 Volt. Natürlich
kommt das auch auf die verwendete Versorgungsspannung an, aber ich gehe hier der Einfachheit halber von 5
Volt aus.

Die in den Computer eingebaute RS-232-Schnittstelle arbeitet nicht mit 0 und 5 Volt, sondern dort üblicherweise
mit -12 bis +12 Volt. Genauer gesagt: -3 bis -25 Volt stellen ein HIGH dar und +3 bis +25 Volt stellen ein LOW
dar. Es ist also nicht nur ein Spannungsunterschied, sondern auch noch ein Verdrehen von HIGH und LOW.
Man setzt deshalb meist einen Pegelwandler (ein IC) zwischen den Computer und den AVR-µController. Oft
wird dafür ein MAX232 eingesetzt. Das ist ein IC mit 16 Pins der die Spannungen anpasst und gleichzeitig noch
invertiert. Auf den meisten Evaluations-Boards ist genau dafür so ein MAX232 mit drauf. Die billigeren
MAX232 arbeiten mit vier Elkos und die etwas teureren MAX232-Exemplare arbeiten mit kleineren 100 nF
Kondensatoren.

Leitungen/Anschlüsse

Einfache Verbindungen in beide Richtungen können schon mit drei Leitungen aufgebaut werden. Braucht man
nur Daten in eine Richtung zu schicken, genügen sogar schon zwei Leitungen.

Computer µC
===================
TX ---------> RX
RX <--------- TX
GND ---------- GND

Interessante Seiten zu diesem Thema:

 http://www.sprut.de/electronic/interfaces/rs232/rs232.htm
 http://avrhelp.mcselec.com/index.html?uart.htm
 http://halvar.at/elektronik/rs232/

Programm auf dem Mikrocontroller

Bei vielen Programmiersprachen ist es üblich, mit dem Befehl PRINT etwas in eine Konsole zu schreiben bzw.
auf dem Bildschirm anzuzeigen. Bei Bascom ist das anders. Ein PRINT gibt nichts in irgendeiner Konsole aus,
sondern schickt die Daten über die UART. Wenn man also einen ATmega8 über einen MAX232 mit dem Com-
puter verbindet und im Programm PRINT "Hallo" schreibt, dann kann man den Text "Hallo" mit jedem be-
liebigen Terminalprogramm empfangen und anzeigen lassen. Der ATmega8 schickt die Daten über den TXD-
Pin (PD1). Dabei wird standardmäßig ein Startbit, ein Stopbit und kein Paritätsbit verwendet. Und üblicherweise
werden 8 Datenbits damit verschickt.

Die Geschwindigkeit der Datenübertragung wird im Programm mit $baud eingestellt. Dieses einfache Pro-
gramm schickt den Text "Hallo" über die UART und beendet sich danach:

$regfile = "M8def.dat"
$crystal = 1000000
$hwstack = 100
$swstack = 100
$framesize = 100
$baud = 4800

Print "Hallo"

End

Da die mit den Fuse-Bits eingestellte Taktfrequenz des ATmega8 bei mir immer noch 1 Mhz ist, wird Bascom
mit $CRYSTAL = 1000000auf diese Frequenz hingewiesen. Falls du eine andere Taktfrequenz eingestellt
hast, musst du $CRYSTAL natürlich anpassen. Bascom kann von sich aus nicht feststellen, wie schnell der µC
getaktet ist. Aber um die Übertragungsgeschwindigkeit genau einhalten zu können, muss Bascom darüber be-
scheid wissen.

Mit $BAUD = 4800 gibt man die Übertragungsgeschwindigkeit an. Und das war es auch schon. Mehr ist nicht
einzustellen. Ab jetzt wird jeder PRINT-Befehl die Daten über die UART raus schicken. Wenn du bei deinen
Tests nur Datenmüll empfängst, dann liegt das zu 99% an einer falschen Einstellung bei $CRYSTAL oder
$BAUD.

Programm auf dem Computer

Als Gegenstelle kann man z.B. das mit Windows mitgelieferte Hyperterminal verwenden.
Die Einstellungen, um die Daten vom ATmega8 empfangen zu können, sehen so aus:

 COM1 bis COM4 (je nach verwendeter COM-Schnittstelle)


 Bits pro Sekunde: 4800
 Datenbits: 8
 Parität: Keine
 Stoppbits: 1
 Flusssteuerung: kein
Natürlich kannst du jedes beliebige Terminalprogramm dafür verwenden. Auch das mit
Bascom mitgelieferte Terminalprogramm. Dieses lässt sich in Bascom über das Menü
"Werkzeuge/Terminal Emulator" aufrufen. Im Terminal-Fenster kommt man über das
Menü "Terminal/Settings" zu den Einstellungen. Allerdings ist es nicht sehr zuverlässig.
Es ist bei mir schon oft vorgekommen, dass es Daten nicht angezeigt hat, nur weil man
vorher den Inhalt des Terminalfensters gelöscht hat. Also wenn du Daten erwartest,
diese aber nicht angezeigt kriegst, dann könnte das am in Bascom eingebauten Terminal-
programm liegen. Verwende in diesem Fall ein anderes Terminalprogramm für den Emp-
fang der Daten.

Das war jetzt mal die eine Richtung, vom µC zum Computer. Über die andere Richtung
schreibe ich beim nächsten mal.

RS232 - MAX232 - ATmega8 (ueber Nullmodemkabel)

Passend zum Beitrag über die Datenübertragung per UART, habe ich mich wieder
mal am Schaltplanzeichnen versucht.

Dieser Schaltplan zeigt, wie man den MAX232 an den ATmega8 über ein Nullmodem-
kabel anschließen kann.
rs232_max232_atmega8_nullmodem_v04.spl

RS232 - MAX232 - ATmega8 (ueber 1 zu 1 Kabel)

Wie ich jetzt feststellen konnte, ist auch beim "Pollin Evaluationsboard" eine D-
Sub Buchse (weiblich) drauf und für den Anschluss über ein normales 1 zu 1 Kabel
gedacht. Man braucht für den Anschluss des Pollin Evaluationsboard ein normales 1
zu 1 D-Sub 9 Kabel mit einer Buchse (weiblich) und einem Stecker (männlich).

Und hier ein möglicher Anschlussplan für "ohne Nullmodemkabel".


rs232_max232_atmega8_v03.spl

UART - RS-232 vom Computer zum Mikrocontroller Teil1

Die Kommunikation über die UART ist ziemlich nützlich und vielseitig. Damit kann der
µC Daten zum Computer oder an andere µC schicken und empfangen. Bascom stellt
dafür mehrere Befehle bereit auf die ich in diesem Beitrag näher eingehen möchte.
Der ATmega8 besitzt eine eingebaute Hardware-UART-Schnittstelle. Man kann UART
aber auch per Software nachbilden. Das ist zwar nicht mehr so schnell wie die Hard-
ware-UART, aber immerhin kann man damit gleichzeitig Verbindung zu mehreren
Gegenstellen aufnehmen. Wenn man nichts besonderes einstellt, dann verwendet
Bascom die Hardware-UART.

$baud
Damit stellt man die gewünschte Übertragungsgeschwindigkeit ein. Diese ist von der
Frequenz abhängig, mit der der µC getaktet wird. Diese muss Bascom
mit $crystal bekannt gegeben werden. Beispiel:
$baud = 9600

Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?baud_1.htm

PRINT
Dieser Befehl sendet eine Zeichenfolge (einen Text) über die UART. Mehrere Zei-
chenfolgen können mit einem Strichpunkt (;) getrennt werden. An diese Zeichen-
folge werden autmatisch die unsichtbaren Zeichen CARRIAGE RETURN (CR) und
LINEFEED (LF) angehängt. Diese unsichtbaren Zeichen kennzeichnen einen Zei-
lenumbruch. Möchte man nicht, dass diese unsichtbaren Zeichen angehängt werden,
dann muss der PRINT-Befehl mit einem Strichpunkt (;) abgeschlossen werden. Wird
an PRINT eine Zahl übergeben, dann wird diese in einen Text umgewandelt. Es wird
also nicht die Zahl übermittelt, sondern der ASCII-Code dafür. Die Zahl 123 besteht
aus den drei Zeichen "1", "2" und "3". Es werden die drei ASCII-Codes für die Zei-
chen "1", "2" und "3" und die ASCII-Codes der beiden unsichtbaren Zeichen CR und
LF übertragen.

Dieser Code

PRINT "Hallo Welt!"

übermittelt die Zeichenfolge "Hallo Welt!" und fügt die unsichtbaren Zeichen CR und
LF an. Das sieht in einem normalen Terminalprogramm so aus:

Hallo Welt!

Analysiert man aber die übermittelten Daten, dann sieht man, dass für jedes Zeichen
der ASCII-Code übermittelt wurde. Hier der Dezimal- und Hex-Code der übermittel-
ten Zeichen:

Dezimal: 72 97 108 108 111 32 87 101 108 116 33 13 10


Hex: 48 61 6C 6C 6F 20 57 65 6C 74 21 0D 0A
Text: H a l l o W e l t ! CR LF

Wenn man diese Codes mit einer ASCII-Tabelle vergleicht, dann erkennt man die Zu-
sammenhänge.

Dieser Code

PRINT 1

übermittelt das Zeichen "1" und nicht die Zahl 1. In einem normalen Terminalpro-
gramm wird also das Zeichen "1" angezeigt. Analysiert man was wirklich übermittelt
wird, dann sieht das so aus:

Dezimal: 49 13 10
Hex: 31 0D 0A
Text: 1 CR LF

Will man die Zahl 1 ohne Umwandlung in ein Zeichen übermitteln, dann nimmt man
dafür den Befehl PRINTBIN.

Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?print.htm

PRINTBIN
Mit PRINTBIN werden die Daten, ohne Umwandlung in einen Text, übermittelt. Fol-
gender Befehl übermittelt wirklich die Zahl 1:

PRINTBIN 1

Analysiert man das was an den Computer gesendet wurde, dann sieht das so aus:

Dezimal: 1
Hex: 01
Text: <unsichtbar>

So können reine Daten, ohne umwandeln, übermittelt werden. Diese lassen sich aber
dafür nicht mehr in einem normalen Terminalprogramm anzeigen. Dafür bräuchte
man einen Anzeigemodus der die empfangenen Daten in Hex oder Dezimal anzeigt.

Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?printbin.htm

INPUT
Der Befehl INPUT erwartet eine Zeichenfolge von der UART und schreibt diese in eine
Variable. INPUT wartet so lange auf die Zeichenfolge bis CARRIAGE RETURN (CR)
übermittelt wurde. Das Programm bleibt also stehen, bis CR über die UART rein
kommt. Das ist ideal, wenn das Programm in der MainLoop nur kommuniziert. Es ist
nicht ideal, wenn in der MainLoop auch noch andere Dinge erledigt werden sollten.
Dafür gibt es andere Befehle wie INKEY oder ISCHARWAITING. Außerdem gibt es so-
gar die Möglichkeit, einen Interrupt auslösen zu lassen, wenn eine Zeichenfolge an-
gekommen ist.

Dieses Beispiel wartet bis eine mit CR abgeschlossene Zeichenfolge übermittelt


wurde und schickt diese über die UART zurück:

Dim Empfangen As String * 20

Do
Input Empfangen
Print "Empfangen: " ; Empfangen
Loop
End

Mit CONFIG INPUT kann man einstellen, welches unsichtbare Zeichen oder welche
unsichtbare Zeichenfolge einen Empfang abschließt. Und mit ECHO OFF kann man
einstellen, dass in ein Terminal eingegebene Zeichen nicht sofort wieder zurück ge-
schickt werden.

Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?input.htm

INPUTBIN
Bei diesem Befehl wartet Bascom auf Binärdaten. Es wartet also nicht auf Text, wie
es INPUT macht, sondern auf reine Daten. An diesen Befehl wird als Argument eine
Variable übergeben. INPUTBIN stoppt das Programm so lange, bis diese Variable be-
füllt wurde. Wird also eine BYTE-Variable übergeben, dann genügt ein Byte und das
Programm läuft weiter. Wird eine WORD-Variable übergeben, dann wartet INPUTBIN
bis zwei Byte (=WORD) übergeben wurden. Beispiel:

Dim My_word_var As Word

Do
Inputbin My_word_var
Printbin My_word_var
Loop

End

Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?inputbin.htm

WAITKEY
Dieser Befehl wartet bis ein Zeichen über die UART zum µC übertragen wurde und
gibt dieses zurück. WAITKEY blockiert so lange bis ein Zeichen übermittelt wurde.
Wird eine BYTE-Variable damit befüllt, dann steht in der BYTE-Variable der ASCII-
Code des übermittelten Zeichens. Wird eine STRING-Variable befüllt, dann weiß Bas-
com, dass der übermittelte ASCII-CODE ein Textzeichen ist und gibt dieses bei PRINT
als solches zurück. Beispiel:

Dim My_byte As Byte


Dim My_string As String * 1

Do
My_byte = Waitkey()
Print "Empfangen: " ; My_byte 'Gibt den ASCII-Code des Zeichens zurück
My_string = Waitkey()
Print "Empfangen: " ; My_string 'Gibt den Text zurück
Loop

End

Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?waitkey.htm

INKEY
Wenn ein Zeichen im zwei Byte großen UART-Buffer steht, dann gibt INKEY ein Zei-
chen zurück. Steht nichts mehr im UART-Buffer, dann gibt INKEY die Zahl 0 zurück.
INKEY bleibt also nicht stehen wie es bei WAITKEY der Fall ist. Das ist oft eine feine
Sache, aber nicht ideal wenn in den zu übermittelnden Daten auch mal ein Nullbyte
vorkommen kann. In so einem Fall sollte man vorher mit ISCHARWAITING prüfen ob
etwas im UART-Buffer steht. Beispiel:

Dim My_string As String * 1

Do
My_string = Inkey()
If My_string <> Chr(0) Then
Print "Empfangen: " ; My_string
End If
Loop

oder so:

Dim My_string As String * 1

Do
If Ischarwaiting() = 1 Then
My_string = Inkey()
Print "Empfangen: " ; My_string
End If
Loop

Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?inkey.htm

ISCHARWAITING
ISCHARWAITING gibt 1 zurück, wenn im UART-Buffer ein Zeichen wartet. ISCHAR-
WAITING gibt 0 zurück, wenn kein Zeichen im UART-Buffer wartet. Beispiel: siehe
INKEY
Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?ischarwai-
ting.htm

ECHO
Die Einstellung ECHO ON gibt an, dass vom Befehl INPUT sofort nach dem Empfang
eines Zeichens, dieses Zeichen zurück geschickt werden soll. Das ist die Stan-
dardeinstellung! Die Einstellung ECHO OFF gibt an, dass nichts zurück geschickt wer-
den soll.

Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?echo.htm

Es gibt noch weitere Befehle zum Arbeiten mit der UART. Die Befehle CONFIG SERIA-
LIN, CONFIG SERIALOUT und CONFIG COM1 werde ich im nächsten Beitrag vorstel-
len.

UART - RS-232 vom Computer zum Mikrocontroller Teil 2

In diesem Beitrag möchte ich mit der Datenübertragung vom Computer zum µC weiter ma-
chen. Diesmal möchte ich näher auf den Befehl CONFIG SERIALIN eingehen. Mit CONFIG
SERIALIN kann man sich die Arbeit mit der UART ein wenig bequemer machen. Anstatt
ständig nachzufragen ob irgendetwas über die UART angekommen ist, kann man den Text
von CONFIG SERIALIN buffern lassen. Weiters kann man sich benachrichtigen lassen, wenn
z.B. ein CARRIAGE RETURN oder ein LINEFEED über die UART rein gekommen ist.

So hat man während der gesamten Kommunikation nichts zu tun. Die Daten werden im Hin-
tergrund empfangen. Und sobald die Daten vollständig im Buffer sind, wird eine vorgegebene
Prozedur ausgeführt in der man auf die empfangenen Daten reagieren kann. Das ist, in meinen
Augen, eine ziemlich feine Sache. Auch wenn man diese Möglichkeit nicht direkt nutzt, so ist
ein Buffer trotzdem etwas Feines. Denn so kann man sicherstellen, dass man keine Daten
übersieht, auch wenn man den Empfang von Daten nicht ständig kontrollieren kann.

CONFIG SERIALIN
Stellt die Hardware-UART so ein, dass ein Buffer für den Empfang verwendet wird.

SIZE

Mit dem SIZE-Parameter wird die Größe des Buffers eingestellt. Der Buffer kann bis zu 255
Zeichen groß sein.

BYTEMATCH

Mit diesem Parameter kann man einstellen ob und wann ein Label oder eine Unterprozedur
angesprungen werden soll. Gibt man ALL an, dann wird das Label oder die Unterprozedur
mit dem Namen "Serial0ByteReceived" ausgeführt sobald ein Zeichen empfangen wurde.
Gibt man einen einstelligen Text (z.B. "a") oder eine Zahl (z.B. 13) an, dann wird das Label
oder die Unterprozedur mit dem Namen "Serial0CharMatch" ausgeführt sobald dieser Text
oder das Zeichen mit der angegebenen ASCII-Nummer empfangen wurde. Die Zahl 13 steht
für CARRIAGE RETURN und die Zahl 10 steht für LINEFEED. Da der Befehl INPUT nor-
malerweise auf CARRIAGE RETURN (13) reagiert, empfiehlt es sich auch bei CONFIG SE-
RIALIN auf dieses unsichtbare Zeichen zu reagieren. Hier ein kleines Beispiel:

$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 100
$swstack = 100
$framesize = 100
$baud = 38400

Declare Sub Serial0charmatch()

Config Serialin = Buffered , Size = 30 , Bytematch = 13


Enable Interrupts

'Hauptschleife
Do
!NOP
Loop
End

Sub Serial0charmatch()
Local Incoming_data As String * 30

'Daten vom Buffer auslesen


Input Incoming_data Noecho
'Ausgelesene Daten zurück schicken
Print Incoming_data
End Sub

In diesem Beispiel wird immer dann, wenn ein CARRIAGE RETURN empfangen wird, die
Unterprozedur "Serial0charmatch" ausgeführt. In dieser Unterprozedur wird mit dem Befehl
INPUT der neue Bufferinhalt in die lokale Variable "Incoming_data" übertragen. NOECHO
kümmert sich darum, dass dabei kein Echo zurück gegeben wird. Da Bascom zum Buffern im
Hintergrund Interrupts braucht, muss man diese mit ENABLE INTERRUPTS aktivieren. In
der Hauptschleife wird nichts gemacht. Das wird mit dem Assembler-Befehl NOP symboli-
siert. NOP steht für "No Operation" und erledigt einen Takt lang "nichts". ;-) Das Rufezeichen
kennzeichnet einen eingebundenen Assembler-Befehl.
Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?config_serialin.htm

CONFIG SERIALOUT
Damit kann man einen Buffer für den Versand von Daten über die UART einrichten.
Wenn man Daten ohne Buffer über die UART schickt, dann bleibt das Programm so
lange stehen bis alle Daten verschickt wurden. Im Gegensatz zum µC selber, ist so
ein Versand sehr, sehr langsam. Um das Programm schneller fortsetzen zu können,
kann man einen Buffer einrichten. Die Daten werden mit PRINT in den Buffer ge-
schrieben. Und falls der Buffer groß genug für die neuen Daten ist, läuft das Pro-
gramm weiter. Die Daten werden im Hintergrund über die UART verschickt.

Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?config_seria-


lout.htm

Binaerdarstellung von IO-Ports

Wir werden nicht mit Binär oder Hex rechnen. Die Binärdarstellung ist einfach nur in-
tuitiver als die Hexdarstellung um auf die I/O-Pins der µC zuzugreifen.

Alle drei Befehle bewirken das Gleiche:

DDRD = &B00010110
DDRD = &H16
DDRD = 22

Bei der Binärdarstellung weiß ich sofort, dass die Pins PD1, PD2 und PD4 als Aus-
gänge konfiguriert wurden. Man zählt von rechts nach links, beginnend mit 0. Das
erste Bit stellt PD0 dar. Das zweite Bit stellt PD1 dar... Aus den Hex- und Dezimal-
zahlen geht das nicht sofort hervor.

Ich werde später mal darauf eingehen, wie man eines der acht Bits auf 1 oder 0 än-
dern kann, ohne alle anderen Bits zu kennen. Aber das war es schon. Mehr werden
wir selten brauchen.

Dieser Kurs soll ja weiterhin so einfach wie möglich und nur so detailliert wie not-
wendig bleiben. Man kann öfter mal etwas durch komplizierteres Programmieren op-
timieren. Aber falls ich entscheiden muss zwischen kompliziertem Code oder einem
etwas besseren µC, dann werde ich meist den besseren µC wählen. Damit hat man
Reserven für spätere Weiterentwicklungen und sauberen Code der sich weiterentwi-
ckeln lässt.

Ausgaenge mit Computer steuern (z.B. LEDs, Relais,...)

Beim letzten mal ging es um die UART und die Verbindung des µC über die RS-232-
Schnittstelle mit dem Computer. Dieses Wissen kann man jetzt nutzen, um mit dem
Computer die Ausgänge (Pins) des µC anzusteuern (z.B. um damit LEDs oder Relais
zu schalten).

Damit das Beispiel nicht zu unübersichtlich wird, möchte ich nur fünf Pins ansteuern.
Das sollte aber genügen. Dieses Beispiel lässt sich mit dem ATmega8 ganz einfach
auf 17 oder, wenn man aufs Ganze geht, sogar auf 20 Ausgänge erweitern.
Der ATmega8 soll auf diese Befehle reagieren:

"SET 2 ON" --> Rückgabe: "OK"


"SET 3 OFF" --> Rückgabe: "OK"
"SET 3 ON" --> Rückgabe: "OK"
"SET ALL ON" --> Rückgabe: "OK"
"SET ALL OFF" --> Rückgabe: "OK"
"GET 5" --> Rückgabe: "0" oder "1"
"GET 4" --> Rückgabe: "0" oder "1"
"GET ALL" --> Rückgabe: z.B. "01001"
"ALL 01001" --> Rückgabe: "OK"
"OK"
"OK"
"OK"
"OK"

Weiters wäre es ideal wenn die Groß-/Kleinschreibung bei diesen Befehlen ignoriert
werden würde.

Und hier das (etwas umfangreichere) Beispiel:

'========================================================================
' Ausgänge des ATmega8 über die UART (RS-232) schalten.
' (by Gerold http://halvar.at)
'
' Kommandos:
' - SET <Ausgangsnummer> ON|OFF
' Einzelne Ausgänge schalten.
' Beispiele: "SET 1 ON" oder "SET 2 OFF"
' Rückgabe: "OK". Bei Fehler "ERROR" oder nichts.
' - SET ALL ON|OFF
' So können in einem Rutsch alle Ausgänge ein- oder ausgeschaltet werden.
' Beispiele: "SET ALL ON" oder "SET ALL OFF"
' Rückgabe: "OK". Bei Fehler "ERROR" oder nichts.
' - GET <Ausgangnummer>
' So wird der Status des angegebenen Ausganges zurück gegeben.
' Rückgabe: "0" oder "1"
' - GET ALL
' Gibt den Status aller Ausgänge zurück.
' Z.B. so: "10010". Diese Ausgabe bedeutet, dass der erste Ausgang und der
' vierte Ausgang ein und die anderen Ausgänge aus sind. Die Zählung beginnt
' links (nicht wie bei Binärzahlen, von rechts).
' - ALL <Status für alle Ausgänge>
' Damit werden in einem Rutsch alle Ausgänge geschaltet.
' Beispiel: "ALL 10010". In diesem Beispiel wird in einem Rutsch der erste
' und der vierte Augang eingeschaltet. Die Ausgänge 2, 3 und 5 werden
' ausgeschaltet.
' Rückgabe: "OK" für jeden Ausgang. Bei Fehler "ERROR" oder nichts.
'========================================================================
$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 100
$swstack = 100
$framesize = 100
$baud = 38400

'Ausgänge
Output_1 Alias Portd.5
Output_2 Alias Portd.6
Output_3 Alias Portd.7
Output_4 Alias Portb.0
Output_5 Alias Portb.1

Config Output_1 = Output


Config Output_2 = Output
Config Output_3 = Output
Config Output_4 = Output
Config Output_5 = Output

'Globale Variablen
Dim Tmp As Byte
Dim New_command As String * 15
Dim Command_array(3) As String * 5
Dim New_status As Bit

'Prozeduren
Declare Sub Serial0charmatch()
Declare Sub Do_get_command(cmd_output As String)
Declare Sub Do_set_command(cmd_output As String , Cmd_status As String)
Declare Sub Do_all_command(cmd_status As String)

'UART-Buffer
Config Serialin = Buffered , Size = 15 , Bytematch = 13
Enable Interrupts

Do
If New_command <> "" Then
'Alles in Großbuchstaben umwandeln
New_command = Ucase(new_command)

'Anweisung aufteilen
Tmp = Split(new_command , Command_array(1) , " ")

Select Case Command_array(1)


Case "GET"
'Status des Ausganges/der Ausgänge zurück geben
Call Do_get_command(command_array(2))
Case "SET"
'Stellt einen oder alle Ausgänge ein
Call Do_set_command(command_array(2) , Command_array(3))
Case "ALL"
'Stellt alle Ausgänge ein
Call Do_all_command(command_array(2))
Case Else
Print "ERROR: unknown command"
End Select

New_command = ""
End If
Loop

End

'Wird automatisch ausgeführt, wenn CARRIAGE RETURN empfangen wird.


Sub Serial0charmatch()
Input New_command Noecho
End Sub

'Gibt den Status des Ausganges/der Ausgänge zurück


Sub Do_get_command(cmd_output As String * 5)
Select Case Cmd_output
Case "ALL"
Print Output_1 ; Output_2 ; Output_3 ; Output_4 ; Output_5
Case "1"
Print Output_1
Case "2"
Print Output_2
Case "3"
Print Output_3
Case "4"
Print Output_4
Case "5"
Print Output_5
Case Else
Print "ERROR: unknown output pin"
End Select
End Sub

'Stellt einen oder alle Ausgänge ein


Sub Do_set_command(cmd_output As String * 5 , Cmd_status As String * 5)
'Neuen Status herausfinden
Select Case Cmd_status
Case "ON"
New_status = 1
Case "OFF"
New_status = 0
Case Else
Print "ERROR: unknown status"
Exit Sub
End Select

'Ausgang/Ausgänge einstellen
Select Case Cmd_output
Case "ALL"
Output_1 = New_status
Output_2 = New_status
Output_3 = New_status
Output_4 = New_status
Output_5 = New_status
Case "1"
Output_1 = New_status
Case "2"
Output_2 = New_status
Case "3"
Output_3 = New_status
Case "4"
Output_4 = New_status
Case "5"
Output_5 = New_status
Case Else
Print "ERROR: unknown output pin"
Exit Sub
End Select
Print "OK"
End Sub

'Stellt alle Ausgänge ein


Sub Do_all_command(cmd_status As String * 5)
Local Status_string As String * 1

'Output_1
Status_string = Mid(cmd_status , 1 , 1)
Select Case Status_string
Case "1"
Output_1 = 1
Print "OK"
Case "0"
Output_1 = 0
Print "OK"
Case Else
Print "ERROR: unknown status"
End Select

'Output_2
Status_string = Mid(cmd_status , 2 , 1)
Select Case Status_string
Case "1"
Output_2 = 1
Print "OK"
Case "0"
Output_2 = 0
Print "OK"
Case Else
Print "ERROR: unknown status"
End Select

'Output_3
Status_string = Mid(cmd_status , 3 , 1)
Select Case Status_string
Case "1"
Output_3 = 1
Print "OK"
Case "0"
Output_3 = 0
Print "OK"
Case Else
Print "ERROR: unknown status"
End Select

'Output_4
Status_string = Mid(cmd_status , 4 , 1)
Select Case Status_string
Case "1"
Output_4 = 1
Print "OK"
Case "0"
Output_4 = 0
Print "OK"
Case Else
Print "ERROR: unknown status"
End Select

'Output_5
Status_string = Mid(cmd_status , 5 , 1)
Select Case Status_string
Case "1"
Output_5 = 1
Print "OK"
Case "0"
Output_5 = 0
Print "OK"
Case Else
Print "ERROR: unknown status"
End Select
End Sub

Dann fange ich mal oben an. Ein wiederverwendbares Programm hat einen Kopf in
dem erklärt wird was das Programm macht und was dazu notwendig ist beziehungs-
weise wie man es bedient. Viele schreiben in den Kopf auch, welche Pins wie ange-
schlossen werden. Das habe ich mir gespart, da ich die Pins im Programm mit aussa-
gekräftigen Namen versehe.

Die Unterprozedur "Serial0charmatch" wird von Bascom automatisch ausgeführt, so-


bald über die UART das Zeichen mit der ASCII-Nummer 13 rein kommt. ASCII-13 ist
CARRIAGE RETURN. Diese Einstellung wird mit dem Befehl CONFIG SERIALIN getrof-
fen.

Die MainLoop habe ich klein gehalten, damit sie übersichtlich bleibt. Alle wichtigen
Dinge werden in ausgelagerten Unterprozeduren erledigt.
Der Programmablauf beginnt, sobald über die UART ein CARRIAGE RETURN rein
kommt. Bascom führt dann die Unterprozedur "Serial0charmatch" aus. In dieser Pro-
zedur wird der Inhalt des UART-Buffers in die globale Variable "New_command" ge-
schrieben.

In der MainLoop wird ständig geprüft ob etwas in dieser Variable steht. Sobald etwas
drinnen steht, wird zuerst der Inhalt der Variable "New_command" mit dem Befehl
UCASE in Großbuchstaben umgewandelt. Zahlen sind davon nicht betroffen.

Da ein Befehl aus zwei oder drei Teilen besteht, die mit einem Leerzeichen (" ") von-
einander getrennt sind, splitte ich diese Teile auf, damit man besser damit arbeiten
kann. Der Befehl SPLIT erledigt das für uns. SPLIT bekommt als erstes Argument
den aufzusplittenden String übergeben. Als zweites Argument muss man ein ARRAY
übergeben, welches mit den einzelnen Textabschnitten befüllt wird. Und als drittes
Argument gibt man an, welche Zeichenfolge die einzelnen Textabschnitte voneinan-
der trennt. Die Variable "Tmp" wird dabei mit der Anzahl der aufgesplitteten Textab-
schnitte befüllt (auch wenn wir diese Information nicht brauchen).

Mit SELECT CASE kann man den µC, je nach Wert einer Variable, etwas bestimmtes
tun lassen. In diesem Fall wird geprüft ob im ersten Array-Element der Text "GET",
"SET" oder "ALL" steht.

Wenn "GET" drinnen steht, dann wird die Unterprozedur "Do_get_command" ausge-
führt. An diese Prozedur wird als Argument die abzufragende Pinnummer (als Text)
oder "ALL" übergeben. Diese Information steht im zweiten Array-Element. In der
Prozedur "Do_get_command" wird mit SELECT CASE geprüft, ob ALL oder eine Pin-
nummer übergeben wurde. Wurde "ALL" übergeben, dann wird der Status aller Aus-
gänge mit PRINT über die UART an den Computer zurück geschickt. Wurde eine Pin-
nummer übergeben, dann wird der Status dieses einen Pins zurück geschickt. Wurde
irgendetwas anderes übergeben, dann wird eine Fehlermeldung zurück geschickt.

Wenn "SET" im ersten Array-Element steht, dann wird die Unterprozedur


"Do_set_command" ausgeführt. An diese Prozedur wird als erstes Argument die Pin-
nummer des zu setzenden Pins oder "ALL" übergeben. Als zweites Argument wird der
gewünschte Status ("ON" oder "OFF") übergeben. In der Prozedur "Do_set_com-
mand" wird zuerst geprüft ob "ON" oder "OFF" übergeben wurde. Bei "ON" wird die
globale BIT-Variable "New_status" auf 1 gesetzt. Bei "OFF" wird diese Variable auf 0
gesetzt. Ich hätte ja gerne eine lokale Variable dafür verwendet, aber das ist eine
Einschränkung von Bascom. Man kann keine BIT-Variablen lokal definieren. In die-
sem Fall ist das kein Problem, da das Programm noch recht übersichtlich ist und
keine Gefahr besteht, dass der Wert der Variable "New_status" von Außen geändert
werden kann. Diese Gefahr könnte bestehen, wenn man diese Variable auch in ei-
nem Interrupt-Handler verwende würde, aber das tun wir ja nicht. Nachdem der
neue Status ermittelt wurde, wird wieder mit SELECT CASE entschieden, welcher
Ausgang neu gesetzt wird.

Wenn "ALL" im ersten Array-Element steht, dann wird die Unterprozedur


"Do_all_command" ausgeführt. An diese Prozedur wird nur der neue, gewünschte
Status übergeben. Das genügt, da wir ja wissen, dass alle Ausgänge neu gesetzt
werden sollen.

In der Prozedur wiederholt sich folgendes Szenario für jeden Pin:


 Mit dem Befehl MID wird aus dem übergebenen Text (z.B. "10010") das erste,
zweite, dritte, vierte oder fünfte Zeichen herausgeholt und in die lokale Vari-
able "Status_string" geschrieben.
 Je nach Wert des "Status_string" ("0" oder "1") wird der Ausgang auf LOW o-
der HIGH gesetzt.
 Mit PRINT "OK" wird der Text "OK" über die UART an den Computer zurück ge-
geben.
 Trat ein Fehler auf, dann wird eine Fehlermeldung zurück gegeben.

Nach dem Ausführen der entsprechenden Unterprozedur wird die Variable


"New_command" wieder zurück gesetzt, damit nicht bei jedem Schleifendurchlauf die
Ausgänge neu gesetzt werden.

Wurde in der MainLoop weder "GET", "SET" oder "ALL" erkannt, dann wird eine Feh-
lermeldung über die UART an den Computer zurück geschickt.

Dieses Programm kann man testen, indem man die Befehle direkt über ein Terminal-
programm an den µC schickt.

Viel Spaß beim Ausprobieren!

20 Ausgaenge mit dem Computer steuern

Wie versprochen, habe ich das letzte Programm ein wenig umgestaltet. Es kommen ein paar
neue Befehle und Techniken darin vor, die ich im Anschluss erkläre. Außerdem habe ich das
Programm aufgebohrt. Es können damit 20 Ausgänge des ATmega8 vom Computer aus fern-
gesteuert werden um damit LEDs, Relais oder andere Dinge zu schalten. :-)

Ich möchte nichts beschönigen. Das ist, im Gegensatz zu den anfänglichen kleinen Pro-
grämmchen, schon richtig harter Stoff. Besonders für Nichtprogrammierer. Es ist nicht
schlimm, wenn du mit diesem Programm noch nicht klar kommst. Schau' dir einfach die
nächsten Kapitel an und lies dieses Programm später noch einmal durch.

Das Programm
'========================================================================
' 20 Ausgänge des ATmega8 über die UART (RS-232) schalten.
' (by Gerold http://halvar.at)
'
' Es wird der eingebaute RC-Oscillator mit 8 Mhz verwendet. So können PB6 und
' PB7 auch als Ausgänge verwendet werden.
'
' Kommandos:
' - SET <Ausgangsnummer> ON|OFF
' Einzelne Ausgänge schalten.
' Beispiele: "SET 1 ON" oder "SET 2 OFF"
' Rückgabe: "OK". Bei Fehler "ERROR" oder nichts.
' - SET ALL ON|OFF
' So können in einem Rutsch alle Ausgänge ein- oder ausgeschaltet werden.
' Beispiele: "SET ALL ON" oder "SET ALL OFF"
' Rückgabe: "OK". Bei Fehler "ERROR" oder nichts.
' - GET <Ausgangnummer>
' So wird der Status des angegebenen Ausganges zurück gegeben.
' Rückgabe: "0" oder "1"
' - GET ALL
' Gibt den Status aller Ausgänge zurück.
' Z.B. so: "10010000000000000000".
' Diese Ausgabe bedeutet, dass der erste Ausgang und der
' vierte Ausgang ein und die anderen Ausgänge aus sind. Die Zählung beginnt
' links (nicht wie bei Binärzahlen, von rechts).
' - ALL <Status für alle Ausgänge>
' Damit werden in einem Rutsch alle Ausgänge geschaltet.
' Beispiel: "ALL 10010000000000000000".
' In diesem Beispiel wird in einem Rutsch der erste
' und der vierte Augang eingeschaltet. Die restlichen Ausgänge werden
' ausgeschaltet.
' Rückgabe: "OK" für jeden Ausgang. Bei Fehler "ERROR" oder nichts.
' - ALL ON | OFF
' So können, wie bereits mit ``SET ALL ON | OFF``, in einem Rutsch alle
' Ausgänge ein- oder ausgeschaltet werden.
' Beispiele: "ALL ON" oder "ALL OFF"
' Rückgabe: "OK" für jeden Ausgang. Bei Fehler "ERROR" oder nichts.
'========================================================================
$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 100
$swstack = 100
$framesize = 100
$baud = 38400

'Ausgänge nach Pin-Reihenfolge (1-28)


Output_1 Alias Portd.2
Output_2 Alias Portd.3
Output_3 Alias Portd.4
Output_4 Alias Portb.6
Output_5 Alias Portb.7
Output_6 Alias Portd.5
Output_7 Alias Portd.6
Output_8 Alias Portd.7
Output_9 Alias Portb.0
Output_10 Alias Portb.1
Output_11 Alias Portb.2
Output_12 Alias Portb.3 'MOSI
Output_13 Alias Portb.4 'MISO
Output_14 Alias Portb.5 'SCK
Output_15 Alias Portc.0
Output_16 Alias Portc.1
Output_17 Alias Portc.2
Output_18 Alias Portc.3
Output_19 Alias Portc.4
Output_20 Alias Portc.5

Config Output_1 = Output


Config Output_2 = Output
Config Output_3 = Output
Config Output_4 = Output
Config Output_5 = Output
Config Output_6 = Output
Config Output_7 = Output
Config Output_8 = Output
Config Output_9 = Output
Config Output_10 = Output
Config Output_11 = Output
Config Output_12 = Output
Config Output_13 = Output
Config Output_14 = Output
Config Output_15 = Output
Config Output_16 = Output
Config Output_17 = Output
Config Output_18 = Output
Config Output_19 = Output
Config Output_20 = Output

'Globale Variablen
Dim Tmp As Byte
Dim New_command As String * 30
Dim Command_array(3) As String * 20

'Prozeduren
Declare Sub Serial0charmatch()
Declare Sub Set_output(byval Outputnr As Byte , Byval Value As Byte)
Declare Sub Do_get_command(byval Cmd_output As String)
Declare Sub Do_set_command(byval Cmd_output As String , Byval Cmd_status As
String)
Declare Sub Do_all_command(byval Cmd_status As String)

'UART-Buffer
Config Serialin = Buffered , Size = 30 , Bytematch = 13

'Interrupts einschalten
Enable Interrupts

Do
If New_command <> "" Then
'Alles in Großbuchstaben umwandeln
New_command = Ucase(new_command)

'Anweisung aufteilen
Tmp = Split(new_command , Command_array(1) , " ")

Select Case Command_array(1)


Case "GET"
'Status des Ausganges/der Ausgänge zurück geben
Call Do_get_command(command_array(2))
Case "SET"
'Stellt einen oder alle Ausgänge ein
Call Do_set_command(command_array(2) , Command_array(3))
Case "ALL"
'Stellt alle Ausgänge ein
Call Do_all_command(command_array(2))
Case Else
Print "ERROR: unknown command"
End Select

'Zurück setzen
New_command = ""
Command_array(1) = ""
Command_array(2) = ""
Command_array(3) = ""
End If
Loop

End

'Wird automatisch ausgeführt, wenn CARRIAGE RETURN empfangen wird.


Sub Serial0charmatch()
Input New_command Noecho
End Sub

Sub Set_output(byval Outputnr As Byte , Byval Value As Byte)


Select Case Outputnr
Case 1 : Output_1 = Value
Case 2 : Output_2 = Value
Case 3 : Output_3 = Value
Case 4 : Output_4 = Value
Case 5 : Output_5 = Value
Case 6 : Output_6 = Value
Case 7 : Output_7 = Value
Case 8 : Output_8 = Value
Case 9 : Output_9 = Value
Case 10 : Output_10 = Value
Case 11 : Output_11 = Value
Case 12 : Output_12 = Value
Case 13 : Output_13 = Value
Case 14 : Output_14 = Value
Case 15 : Output_15 = Value
Case 16 : Output_16 = Value
Case 17 : Output_17 = Value
Case 18 : Output_18 = Value
Case 19 : Output_19 = Value
Case 20 : Output_20 = Value
Case Else
Print "ERROR: unknown output pin"
End Select
End Sub

'Gibt den Status des Ausganges/der Ausgänge zurück


Sub Do_get_command(cmd_output As String * 3)
Select Case Cmd_output
Case "ALL"
Print Output_1 ; Output_2 ; Output_3 ; Output_4 ; Output_5 ; Output_6 ; _
Output_7 ; Output_8 ; Output_9 ; Output_10 ; Output_11 ; Output_12 ; _
Output_13 ; Output_14 ; Output_15 ; Output_16 ; Output_17 ; Output_18
; _
Output_19 ; Output_20
Case "1" : Print Output_1
Case "2" : Print Output_2
Case "3" : Print Output_3
Case "4" : Print Output_4
Case "5" : Print Output_5
Case "6" : Print Output_6
Case "7" : Print Output_7
Case "8" : Print Output_8
Case "9" : Print Output_9
Case "10" : Print Output_10
Case "11" : Print Output_11
Case "12" : Print Output_12
Case "13" : Print Output_13
Case "14" : Print Output_14
Case "15" : Print Output_15
Case "16" : Print Output_16
Case "17" : Print Output_17
Case "18" : Print Output_18
Case "19" : Print Output_19
Case "20" : Print Output_20
Case Else
Print "ERROR: unknown output pin"
End Select
End Sub

'Stellt einen oder alle Ausgänge ein


Sub Do_set_command(cmd_output As String * 3 , Cmd_status As String * 3)
Local Outputnr As Byte
Local Value As Byte

'Neuen Status herausfinden


Select Case Cmd_status
Case "ON"
Value = 1
Case "OFF"
Value = 0
Case Else
Print "ERROR: unknown status"
Exit Sub
End Select

'Ausgang/Ausgänge einstellen
Select Case Cmd_output
Case "ALL"
For Outputnr = 1 To 20
Call Set_output(outputnr , Value)
Next Outputnr
Case Else
Outputnr = Val(cmd_output)
If Outputnr > 0 Then ' Wenn kein Fehler beim Umwandeln passiert ist
Call Set_output(outputnr , Value)
Else
Print "ERROR: unknown output pin"
Exit Sub
End If
End Select
Print "OK"
End Sub

'Stellt alle Ausgänge ein


Sub Do_all_command(cmd_status As String * 20)
Local Status_string As String * 1
Local Outputnr As Byte

Select Case Cmd_status


Case "ON"
For Outputnr = 1 To 20
Call Set_output(outputnr , 1)
Print "OK"
Next Outputnr
Case "OFF"
For Outputnr = 1 To 20
Call Set_output(outputnr , 0)
Print "OK"
Next Outputnr
Case Else
If Len(cmd_status) = 20 Then
For Outputnr = 1 To 20
Status_string = Mid(cmd_status , Outputnr , 1)
Select Case Status_string
Case "1"
Call Set_output(outputnr , 1)
Print "OK"
Case "0"
Call Set_output(outputnr , 0)
Print "OK"
Case Else
Print "ERROR: unknown status"
End Select
Next Outputnr
Else
Print "ERROR: unknown status"
End If
End Select
End Sub

Die Variable "New_command" ist jetzt größer geworden, da jetzt der Status beim Be-
fehl ALL 20 Zeichen lang ist. "Command_array" wurde deshalb ebenfalls vergrößert. Auch
CONFIG SERIALIN wurde entsprechend angepasst.

Wenn man sich die Deklarationen der Unterprozeduren ansieht, dann fallen zwei Dinge auf.
Ich verwende "byval" und bei "As String" fehlt die Angabe der Textlänge.

Das ist jetzt ein bischen kompliziert. :-| Der Zusatz "byval" kennzeichnet einen Parameter als
"Wertparameter". Das heißt, dass beim Aufruf der Unterprozedur der Wert einer Variable in
eine neue Variable (in einen neuen Speicherbereich) kopiert wird. Gibt man "byval" nicht an,
dann wird standardmäßig "byref" verwendet. Mit "byref" wird der Wert nicht in einen neuen
Speicherbereich kopiert, sondern nur eine Referenz zum Speicherbereich übergeben in dem
der Wert liegt. "byval" kapselt die Unterprozedur besser vom Rest des Programmes ab und
hilft damit Fehler zu vermeiden. "byref" (der Standard) kapselt den Parameter nicht vom Rest
des Programmes ab. Änderungen dieses Parameters (dieser Variable) wirken sich auch außer-
halb der Unterprozedur aus. Umgekehrt ist es natürlich genau so. Ein mit "byref" deklarierter
Parameter kann sich (z.B. durch einen Interrupt) ändern, wärend die Unterprozedur abgearbei-
tet wird. Das kann wiederum unerklärliche Fehler hervorrufen. Dafür ist "byref" schneller als
"byval", da die Arbeit des Kopierens wegfällt.
So lange du nicht optimieren musst, nimm "byval"!

Und was den zweiten Puntk betrifft, kann ich dir keine Erklärung dazu bieten. Man kann die
Textlänge beim Deklarieren nicht einstellen. Macht man es, dann bekommt man einen Fehler
beim Kompilieren des Programms. Mehr weiß ich auch nicht darüber.

Hilfe findest du unter:

 http://avrhelp.mcselec.com/index.html?declare_sub.htm
 http://avrhelp.mcselec.com/index.html?sub.htm
 http://avrhelp.mcselec.com/index.html?call.htm

Es ist eine neue Unterprozedur dazu gekommen. "Set_output" erleichtert das Arbeiten mit den
vielen Ausgängen. Besonders für die neu eingeführte FOR-NEXT-Schleife.

FOR-NEXT-Schleife
Die FOR-NEXT-Schleife (auch FOR-Schleife genannt) ist ein Befehlskonstrukt, mit dem man
Programmcode mehrmals hintereinander ausführen kann, ohne diesen Programmcode mehr-
mals schreiben zu müssen. Der große Vorteil der FOR-NEXT-Schleife ist, dass man in einer
Variable einen Zähler bei jedem Schleifendurchlauf erhöhen kann. Dieser Zähler (eine Zahl)
kann in der Schleife verwendet werden.

So eine FOR-NEXT-Schleife sieht ungefähr so aus:

FOR <variablename> = <beginn> TO <ende>


<Programmcode, der mehrmals ausgeführt wird>
NEXT <variablename>

Beispiel:

DIM My_bytevar AS BYTE


FOR My_bytevar = 1 TO 10
PRINT "Wert der Variable 'My_bytevar': " ; My_bytevar
NEXT My_bytevar

Diese FOR-Schleife wird zehn mal ausgeführt. Beim Ersten Durchlauf hat die Variable
"My_bytevar" den Wert 1. Beim zweiten Durchlauf hat sie den Wert 2. Usw.

Die Bascom-Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?for_next.htm

Weitere Erklärungen zum Code


'Zurück setzen
New_command = ""
Command_array(1) = ""
Command_array(2) = ""
Command_array(3) = ""

In der Hauptschleife hat sich kaum etwas geändert. Dort setze ich nur zusätzlich
auch den Inhalt der Arrayelemente zurück. Damit versuche ich unvorhersehbare Ne-
beneffekte zu vermeiden.

Sub Set_output(byval Outputnr As Byte , Byval Value As Byte)


Select Case Outputnr
Case 1 : Output_1 = Value
Case 2 : Output_2 = Value
...
Case 19 : Output_19 = Value
Case 20 : Output_20 = Value
Case Else
Print "ERROR: unknown output pin"
End Select
End Sub

Da man für den Zugriff auf einen der Ausgänge den Namen des Ausganges (Out-
put_1 bis Output_20) kennen muss hilft die neue Unterprozedur "Set_output" dabei,
den Ausgang zu setzen wenn man nur die Ausgangsnummer aber nicht den Namen
weiß. In einer FOR-Schleife hat man eine Zahl zur Verfügung die sich bei jedem
Schleifendurchlauf hoch zählt. Um nicht in jeder FOR-Schleife mit SELECT CASE den
gewünschten Ausgang bestimmen zu müssen, wurde dieser Code in diese neue Un-
terprozedur ausgelagert. An diese Unterprozedur wird die Nummer des Ausganges
übergeben. Anhand dieser Nummer wird der gewünschte Ausgang mit SELECT CASE
bestimmt und der neue Statuswert (0 oder 1) zugewiesen. Da man als Parameter ei-
ner Unterprozedur keine BIT-Variable verwenden kann, wurde dort eine BYTE-Vari-
able verwendet. Das ist aber kein Problem, da beim Zuweisen eines Wertes an "Out-
put_1" bis "Output_20" automatisch nur das erste Bit (von rechts gezählt) der Byte-
Variable übergeben wird. Statt &B00000001 wird nur das erste Bit (rechts au-
ßen) &B1 zugewiesen. Das ist sehr praktisch und erstpart uns das Auslesen dieses
Bits. Das ist natürlich Speicherverschwendung -- aber wer hat, der kann. :-)

Sub Do_get_command(cmd_output As String * 3)


Select Case Cmd_output
Case "ALL"
Print Output_1 ; ... Output_20
Case "1" : Print Output_1
...
Case "20" : Print Output_20
Case Else
Print "ERROR: unknown output pin"
End Select
End Sub

Die Unterprozedur "Do_get_command" wurde nur um die zusätzlichen Ausgänge er-


weitert.

Sub Do_set_command(cmd_output As String * 3 , Cmd_status As String * 3)


Local Outputnr As Byte
Local Value As Byte

'Neuen Status herausfinden


Select Case Cmd_status
Case "ON"
Value = 1
Case "OFF"
Value = 0
Case Else
Print "ERROR: unknown status"
Exit Sub
End Select

'Ausgang/Ausgänge einstellen
Select Case Cmd_output
Case "ALL"
For Outputnr = 1 To 20
Call Set_output(outputnr , Value)
Next Outputnr
Case Else
Outputnr = Val(cmd_output)
If Outputnr > 0 Then ' Wenn kein Fehler beim Umwandeln passiert ist
Call Set_output(outputnr , Value)
Else
Print "ERROR: unknown output pin"
Exit Sub
End If
End Select
Print "OK"
End Sub

Die Unterprozedur "Do_set_command" wurde ein wenig umgebaut. Die lokale Vari-
able "Outputnr" wird für die FOR-Schleife zum Hochzählen benutzt. Die lokale BYTE-
Variable "Value" ersetzt die im alten Programm verwendete BIT-Variable "New_sta-
tus". Wird an den Parameter "Cmd_output" der Wert "ALL" übergeben, dann wird in
einer FOR-Schleife jeder der zwanzig Ausgänge durchlaufen und ein- oder ausge-
schaltet. Steht in der Variable "Cmd_output" nicht "ALL", dann wird erwartet, dass
eine Zahl übergeben wurde. Also wird zuerst versucht, "cmd_output" mit dem Befehl
VAL in eine Zahl umzuwandeln. Wurde keine Zahl erkannt, dann wird damit in die
Variable "Outputnr" die Zahl 0 geschrieben. Wird eine Zahl erkannt, dann wird diese
in "Outputnr" geschrieben. Der Rest der Unterprozedur sollte sich selbst erklären.

Sub Do_all_command(cmd_status As String * 20)


Local Status_string As String * 1
Local Outputnr As Byte

Select Case Cmd_status


Case "ON"
For Outputnr = 1 To 20
Call Set_output(outputnr , 1)
Print "OK"
Next Outputnr
Case "OFF"
For Outputnr = 1 To 20
Call Set_output(outputnr , 0)
Print "OK"
Next Outputnr
Case Else
If Len(cmd_status) = 20 Then
For Outputnr = 1 To 20
Status_string = Mid(cmd_status , Outputnr , 1)
Select Case Status_string
Case "1"
Call Set_output(outputnr , 1)
Print "OK"
Case "0"
Call Set_output(outputnr , 0)
Print "OK"
Case Else
Print "ERROR: unknown status"
End Select
Next Outputnr
Else
Print "ERROR: unknown status"
End If
End Select
End Sub
Die Unterprozedur "Do_all_command" hat sich mit der Einführung der FOR-Schleife
am radikalsten verkürzt. Sie war im ursprünglichen Programm ja schon groß, aber
bei 20 Ausgängen wäre sie ohne die FOR-Schleife riesig geworden. Wird "ON" oder
"OFF" erkannt, dann wird in jeweils einer FOR-Schleife jeder Ausgang durchlaufen
und ein- oder ausgeschaltet. Ansonsten wird geprüft ob der Statustext 20 Zeichen
lang ist. Und wenn das der Fall ist, dann wird in einer FOR-Schleife jedes Zeichen des
Statustextes (cmd_status) durchlaufen. Und wieder wird mit einem Aufruf der Unter-
prozedur "Set_output" jeder der Ausgänge ein- oder ausgeschaltet.

3x4 Tastenfeld

Es ist die Frage aufgetaucht, wie man ein typisches 3x4 Tastenfeld (eine Nummern-
tastatur) mit Bascom einlesen kann.

Bascom hat dafür bereits einen HighLevel-Befehl eingebaut. So etwas wie DE-
BOUNCE, nur für eine Tastenmatrix bis zu 4x4 Tasten (oder sogar bis 6x4 Tasten) --
> GETKBD. Ich habe mir vor kurzem so ein Tastenfeld bei Conrad besorgt, welches
ich zum Testen verwende. Wenn jemand einen Händler weiß, der diese Dinger güns-
tiger verkauft (auch nach Österreich), dann bitte her mit der Information. :-) Denn
über 6 Euro für so etwas ist doch etwas viel. Erst recht, wenn man es nur zum Tes-
ten braucht.

Das Datenblatt dieses 3x4-Tastenfeldes kann man sich bei Conrad herunterladen.

Die Spalte mit der "1" ist die erste Spalte. Die Spalte mit der "2" ist die zweite Spalte
und die Spalte mit der "3" ist die dritte Spalte. Die Zeile mit der "1" ist die erste
Zeile. Die Zeile mit der "4" ist die zweite Zeile. Die Zeile mit der "7" ist die dritte
Zeile und die Zeile mit dem Stern ("*") ist die vierte Zeile. Mit dieser Erklärung
möchte ich Verwechslungen vorzeitig ausschließen. -- Bitte nicht lachen, das ist alles
schon passiert.
Für so eine Tastaturmatrix wird ein ganzer Port (8 Pins) benötigt. Auch wenn man für
diese 3x4 Tastaturmatrix nur sieben Pins braucht. Aber der achte Pin wird bei jedem
Durchlauf ebenfalls abgefragt. Da wir für die UART-Verbindung die beiden Pins PD0
und PD1 brauchen, bleibt nur mehr der PORT B übrig. Da beim ATmega8 ein exter-
ner Quarz an PB6 und PB7 angeschlossen wird, bleibt uns nichts anderes übrig, als
auf den externen Quarz zu verzichten. Wer einen ATmega16 oder ATmega32 hat,
kann dort natürlich auch andere, noch freie PORTs verwenden.

Der Bascom-Befehl GETKBD ist für die Abfrage so einer Tastaturmatrix zuständig.
Und mit CONFIG KBD stellt man den Port ein, an dem die Tastaturmatrix angeschlos-
sen wurde. Wer keinen ganzen Port übrig hat, muss auf diese HighLevel-Befehle ver-
zichten und die Tastenabfrage händisch programmieren. Wie so etwas geht, wird im
Buch "Programmieren der AVR RISC Mikrocontroller mit Bascom-AVR" von Claus
Kühnel genau erklärt.
Wenn du GETKBD nachbilden möchtest, dann musst du eine Routine schreiben die in
etwa so vorgeht: Zuerst werden die Pins der Zeilen als Eingang mit internem PullUp-
Widerstand definiert. Die Pins der Spalten werden als Ausgänge definiert und auf
HIGH gesetzt. Zum Testen wird zuerst der Ausgang für die Spalte 1 auf LOW gesetzt.
Dann wird geprüft ob der Eingang für die erste Zeile LOW oder HIGH ist (nimmt man
dafür DEBOUNCE, dann wird es einfacher). Dann der Eingang für die zweite Zeile,
dann die dritte und zuletzt die vierte Zeile. Dann wird der Ausgang der Spalte 1 wie-
der auf HIGH gesetzt und das Spiel beginnt mit der zweiten Spalte wieder von vorne.
Irgendwann ist man alle Zeilen-/Spalten-Kombinationen durch und weiß welche
Taste gedrückt wurde.

CONFIG KBD
Mit diesem Befehl stellt man den Port ein, an den die Tastaturmatrix angeschlossen
wurde. Man kann damit die Matrix auch auf 6x4 Tasten erweitern, die DEBOUNCE-
Zeit und ein Delay einstellen.

Beispiel:

Config Kbd = Portb , Debounce = 20 , Delay = 10

Diese Einstellung liefert bei meiner Conrad-3x4-Tastaturmatrix ein recht gutes Er-
gebnis. Man muss sich natürlich ein wenig spielen um die optimale Einstellung für
das eigene Tastastenfeld heraus zu finden. Zuallererst würde ich es einfach mal ohne
die Parameter Debounce und Delay probieren.

Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?con-


fig_kbd.htm

GETKBD
Dieser Befehl ist dafür zuständig, die Tastaturmatrix abzufragen und gibt einen
Keycode für die empfangene Taste zurück. Wird kein Tastendruck erkannt, dann wird
als Keycode die Zahl 16 zurück gegeben. Es wird also nicht der Text zurück gegeben,
der auf der Taste steht, sondern es wird eine Zahl zurück gegeben, die die gedrückte
Taste identifiziert. Diese Taste muss später noch dem Text, der auf der Taste steht,
zugeordnet werden.

Beispiel:

$regfile = "m8def.dat"
$crystal = 1000000
$hwstack = 100
$swstack = 100
$framesize = 100
$baud = 2400

Dim Keycode As Byte


Config Kbd = Portb , Debounce = 20 , Delay = 10

Do
Keycode = Getkbd()
If Keycode <> 16 Then
Print Keycode
Waitms 200
End If
Loop

End

GETKBD scannt in der MainLoop den mit CONFIG KBD angegebenen Port nach einem
Tastendruck. Immer wenn nichts gedrückt wurde, dann wird der Keycode 16 zurück
geliefert.

Vorsicht!
Achtung! Der Bascom-Simulator liefert 15 zurück -- warum auch immer.

Der gescannte KeyCode wird mit Print verschickt. Wichtig in diesem Code ist die
Zeile WAITMS 200 , die sich darum kümmert, dass bei einem kurzen Tastendruck nicht
hunderte mal der Keycode abgefragt und über die UART übertragen wird.

Bascom Hilfe zu diesem Befehl: http://avrhelp.mcselec.com/index.html?getkbd.htm

Zur Erinnerung! GETKBD() liefert nicht das zurück, was auf der Tastatur steht, son-
dern die Position der Taste.

 Taste "1" = Keycode 0


 Taste "2" = Keycode 1
 Taste "3" = Keycode 2
 Taste "4" = Keycode 4
 Taste "5" = Keycode 5
 Taste "6" = Keycode 6
 Taste "7" = Keycode 8
 Taste "8" = Keycode 9
 Taste "9" = Keycode 10
 Taste "*" = Keycode 12
 Taste "0" = Keycode 13
 Taste "#" = Keycode 14
 keine Taste = Keycode 16
Die Einfachste Art, aus dem Keycode den Text der Taste zurück zu bekommen ist
MID. Der Befehl MID kann aus einem Text einen Teil rausholen. Und das sieht als
Beispiel so aus:

$regfile = "m8def.dat"
$crystal = 1000000
$hwstack = 100
$swstack = 100
$framesize = 100
$baud = 2400

Dim Keycode As Byte


Dim Asciichars As String * 16

Config Kbd = Portb , Debounce = 20 , Delay = 10

Asciichars = "123 456 789 *0# "

Do
Keycode = Getkbd()
If Keycode <> 16 Then
Print Keycode
Keycode = Keycode + 1
Print Chr(34) ; Mid(asciichars , Keycode , 1) ; Chr(34)
Waitms 200
End If
Loop

End

MID beginnt mit der Zählung bei 1 und nicht bei 0. Deshalb muss man die Rückgabe
des Befehls GETKBD zuerst um 1 erhöhen. Bascom Hilfe zu diesem Be-
fehl: http://avrhelp.mcselec.com/index.html?mid.htm

CHR gibt ein Zeichen zurück wenn man es mit ASCII-Code füttert. 34 ist der ASCII-
Code für das doppelte Anführungszeichen ("). Bascom Hilfe zu diesem Be-
fehl: http://avrhelp.mcselec.com/index.html?chr.htm

Das ist mein Versuchsaufbau:


LCD - Liquid Crystal Display - Textanzeige

In diesem Beitrag möchte ich darüber berichten wie man LCDs (Liquid Crystal Dis-
plays) mit Bascom ansteuert. Damit meine ich die ein- oder mehrzeiligen Textdis-
plays (alphanumerisch), nicht die Grafikdisplays. Die Grafikdisplays sind etwas
schwieriger zu handhaben und sind vielleicht mal Thema eines anderen Beitrags.

Gleich vorab. Eigentlich ist es sehr einfach. Man sucht in der Bascom-Hilfe nach
"LCD", liest sich alles durch, schließt das LCD an und fertig. Alles was ich noch bieten
kann, ist ein kleiner Bericht der aufzeigt wie ich es gemacht habe und vielleicht noch
eine kleine Auflistung der wichtigsten LCD-Befehle.

Viele Text-LCDs verwenden den LCD-Controller HD44780. Diese und alle kompatib-
len Displays werden von Bascom voll unterstützt. Solche LCDs bekommt man ziem-
lich günstig bei Pollin. Wenn man dort im Shop nach "Industriestandard" sucht, dann
findet man ein paar wirklich günstige LCDs, die mit Bascom funktionieren sollten.

Gängige LCD-Controller:

 HD44780/HDD44780 (Industriestandard, kein Problem mit Bascom)


 SED1278 (kompatibel zum HD44780, kein Problem mit Bascom)
 KS0073 (teilweise kompatibel zum HD44780, kein Problem mit Bascom)
 LC7985 (kompatibel zum HD44780)
 M50530 (???)
 SED1531 (???)

Es gibt zwei Möglichkeiten wie Bascom ein LCD ansteuert. Entweder über einen Da-
tenbus (BUS-Modus) oder über auswählbare Pins eines Ports (PORT-Modus).
Der BUS-Modus ist nur dann möglich, wenn man z.B. auch einen externen Speicher
an den AVR-µC anhängen kann (Bus Interface). Dann teilen sich der externe Spei-
cherbaustein und das LCD die Leitungen. Der ATmega8 hat keinen solchen Bus. Für
uns ist nur der PORT-Modus interessant. Im PORT-Modus können wir selber bestim-
men an welchen Pins das LCD hängt. Dafür braucht dieser Modus aber auch etwas
mehr Programmspeicher im µC.

Die Daten können an das LCD über 8 oder 4 µC-Pins übermittelt werden (8-Bit Mo-
dus bzw. 4-Bit Modus). Der 8-Bit Modus ist ein wenig schneller. Dafür werden im 4-
Bit Modus nicht so viele µC-Pins belegt.

Die LCDs mit HD44780-Controller stellen normalerweise diese Anschlüsse bereit:

01 -- GND (VSS)
02 -- 5 V (VCC oder VDD)
03 -- Kontrast (VO oder VEE)
04 -- RS (Register Select)
05 -- R/W (Read/Write)
06 -- E (Enable)
07 -- D0 (Datenbit 0, im 4-Bit Modus nach GND schalten)
08 -- D1 (Datenbit 1, im 4-Bit Modus nach GND schalten)
09 -- D2 (Datenbit 2, im 4-Bit Modus nach GND schalten)
10 -- D3 (Datenbit 3, im 4-Bit Modus nach GND schalten)
11 -- D4 (Datenbit 4)
12 -- D5 (Datenbit 5)
13 -- D6 (Datenbit 6)
14 -- D7 (Datenbit 7)
15 -- evt. Hintergrundbeleuchtung, siehe Datenblatt
16 -- evt. Hintergrundbeleuchtung, siehe Datenblatt

Anschluss an den ATmega8:

LCD-Pin 1 (VSS) --> GND


LCD-Pin 2 (VDD) --> 5 V Versorgungsspannung
LCD-Pin 3 (Kontrast) --> z.B. Poti/Trimmer für Kontrasteinstellung
LCD-Pin 4 (RS) --> an einen Pin des ATmega8 (z.B. PORTD.2)
LCD-Pin 5 (R/W) --> GND
LCD-Pin 6 (E) --> an einen Pin des ATmega8 (z.B. PORTD.3)
LCD-Pin 7 (D0) --> im 4-Bit Modus nach GND schalten
LCD-Pin 8 (D1) --> im 4-Bit Modus nach GND schalten
LCD-Pin 9 (D2) --> im 4-Bit Modus nach GND schalten
LCD-Pin 10 (D3) --> im 4-Bit Modus nach GND schalten
LCD-Pin 11 (D4) --> an einen Pin des ATmega8 (z.B. PORTD.4)
LCD-Pin 12 (D5) --> an einen Pin des ATmega8 (z.B. PORTD.5)
LCD-Pin 13 (D6) --> an einen Pin des ATmega8 (z.B. PORTD.6)
LCD-Pin 14 (D7) --> an einen Pin des ATmega8 (z.B. PORTD.7)
LCD-Pin 15 (HG-Bel.) --> siehe Datenblatt
LCD-Pin 16 (HG-Bel.) --> siehe Datenblatt

Leider gibt es große Unterschiede zwischen den Displays, was die Temperaturabhän-
gigkeit betrifft. LCDs für Normaltemperaturen arbeiten meist mit einer positiven
Spannung am VO-Pin (Kontrast). Und Displays mit erweitertem Temperaturbereich
oder große Displays, brauchen meist eine negative Spannung am VO-Pin. Leider fin-
det man gerade bei Billig-LCDs erst beim Anschließen heraus, mit welcher Spannung
der VO-Pin versorgt werden muss.

Ziemlich gute Informationen bekommt man unter http://www.sprut.de/electro-


nic/lcd/index.htm. Also wenn dein Display nicht so funktioniert wie es funktionieren
sollte, dann lohnt es sich, dort mal vorbei zu schauen und sich geballtes Wissen über
die LCDs rein zu ziehen. Und zum Erstellen der evt. benötigten negativen Spannung,
hat sprut.de auch etwas zu sagen: http://www.sprut.de/electronic/switch/mi-
nus.html

Das Problem mit der negativen Spannung umgeht man am einfachsten, mit einem
neuen LCD. ;-) Für meine ersten Tests verwende ich das bei Pollin gekaufte "Power-
tip PC 1602-F". Leider ist Pollin nicht gerade verlässlich, was die Verfügbarkeit der
Produkte betrifft. Es kann also leicht sein, dass dieses LCD nicht erhältlich ist wenn
du bei Pollin danach suchst.
Die normalen LCD-Routinen von Bascom nutzen den R/W-Pin nicht. Es wird einfach
lange genug gewartet, so dass Bascom das LCD nicht auslesen muss. Die Mindest-
wartezeiten sind im HD44780-Datenblatt aufgeführt. Das funktioniert ziemlich gut.

Der R/W-Pin muss nur dann beschaltet werden, wenn man in Bascom die LCD-Routi-
nen einer Zusatz-Library, wie z.B. die LCD4BUSY-Library, nutzt. Das bringt's aber
nur, wenn das LCD sehr schnell reagieren soll. Z.B. wenn sich die Anzeige nicht nur
alle Sekunden, sondern im Millisekundenbereich ändern soll. Dann könnte es auch
sehr langsame LCDs geben, die sich nicht an das ausgemachte Timing halten. Dann
kann man den R/W-Pin in Verbindung mit der LCD4BUSY-Library ebenfalls nutzen.

Aber im Normalbetrieb und so lange es keine Probleme damit gibt, ist es nicht not-
wendig den R/W-Pin an den µC anzuschließen. Zumindest nicht, wenn man Bascom
nutzt.

Die Ansteuerung eines LCD läuft unter Bascom ähnlich einfach wie die Ausgabe von
Text mit dem Befehl PRINT . Statt mit PRINT an die UART, schickt man den Text mit
dem Befehl LCD an das Display. Zusätzlich gibt es noch ein paar Befehle um den Cur-
sor auf dem Display zu positionieren und das Display zu löschen. Aber viel mehr ist
nicht dahinter. Das Schwierigste an der Sache ist, das Display richtig anzuschließen
und in Bascom richtig zu konfigurieren.

Konfiguration
Zum Konfigurieren des LCD stellt Bascom ein paar Befehle zur Verfügung. Da für uns
nur der PORT-Modus interessant ist, gehe ich nicht näher auf die Befehle für den
BUS-Modus ein. Wenn man nicht toll herumkonfiguriert, dann nimmt Bascom an,
dass wir das LCD im 4-Bit PORT-Modus ansprechen möchten.

CONFIG LCDPIN

Mit diesem Befehl wird Bascom mitgeteilt, welche Pins zur Ansteuerung des LCDs
verwendet werden. Das würde für den oben gezeigten Schaltplan so aussehen:

Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , _


Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2

Die Hilfe zu diesem Befehl findet man hier: http://avrhelp.mcselec.com/in-


dex.html?config_lcdpin.htm

CONFIG LCD

Damit gibt man den Typ des Displays an. Gemeint ist die Anzahl an Zeilen und Spal-
ten. Mögliche Einstellungen sind:

 16 * 1, 16 * 1a, 16 * 2 (Standard), 16 * 4
 20 * 2, 20 * 4, 20 * 4A
 40 * 4

Weiters kann man mit diesem Befehl auch die Verwendung eines anderen Chipsatzes
einstellen. Neben dem Standard (HD44780) kann man noch zwischen "KS077" und
"DOGM" wählen. Nähere Infos stehen in der Hilfe zu diesem Befehl.

Beispiel:

Config Lcd = 16 * 2
Die Hilfe zu diesem Befehl findet man hier: http://avrhelp.mcselec.com/in-
dex.html?config_lcd.htm

Befehle
Die Anzeige des LCD bestimmen hauptsächlich die vier Befehle CURSOR , CLS , LO-
CATE und LCD . Es gibt noch weitere LCD-Befehle wie z.B. HOME , SHIFTLCD , SHIFTCUR-
SOR , DISPLAY und INITLCD . Nähere Infos darüber findest du in der Bascom-Hilfe. Wei-
ters sollte man wissen, dass die Ausgabe auf ein LCD ziemlich langsam ist und des-
halb der Befehl LCD nicht in einem Interrupt-Handlereingesetzt werden sollte.

CURSOR

Mit diesem Befehl stellt man das Verhalten des Cursors ein. Der Cursor ist bei LCDs
meist ein Unterstrich. Diesen kann man ein-/ausschalten und blinken lassen.

Beispiel:

CURSOR OFF
CURSOR ON BLINK

Die Hilfe zu diesem Befehl findet man hier: http://avrhelp.mcselec.com/in-


dex.html?cursor.htm

CLS

CLS löscht das Display und setzt den Cursor in die linke, obere Ecke.

Die Hilfe zu diesem Befehl findet man hier: http://avrhelp.mcselec.com/in-


dex.html?cls.htm

LOCATE

LOCATE setzt den Cursor in die angegebene Zeile und Spalte.

Beispiel:

LOCATE 1, 1 ' Zeile 1 Spalte 1


LOCATE 2, 10 ' Zeile 2 Spalte 10

Statt LOCATE 1, 1 könnte man auch den Befehl HOME verwenden.

Die Hilfe zu diesem Befehl findet man hier: http://avrhelp.mcselec.com/in-


dex.html?locate.htm
INITLCD

Dieser Befehl initialisiert das Display. INITLCD braucht man im Normalfall nicht, da
Bascom das Display automatisch beim Start initialisiert. Aber INITLCD bietet sich an,
um zwischendurch das Display zurück zu setzen, falls etwas (z.B. ein Relais) die Dis-
playausgabe gestört hat. Das Initialisieren dauert ein wenig und sieht manchmal
auch nicht besonders gut aus. Deshalb sollte man es nicht übertreiben und das Dis-
play nur nur im Notfall alle paar Minuten und im Normalfall nur alle paar Stunden
neu initialisieren. Wie oft du INITLCD wirklich einsetzt, musst du natürlich selber ent-
scheiden. Wenn Störungen öfter auftreten, dann solltest du dir vielleicht lieber Ge-
danken über eine bessere Entstörung der Schaltung machen.

Die Hilfe zu diesem Befehl findet man hier: http://avrhelp.mcselec.com/in-


dex.html?initlcd.htm

LCD

Mit diesem Befehl schreibt man auf das Display. LCD verhält sich sehr ähnlich
dem PRINT -Befehl und kann meist genau so eingesetzt werden. Statt mit PRINT auf
die UART, schreibt man mit LCD auf das Display.

Beispiel:

CLS
LCD "Zeile 1"
LOCATE 2, 1
LCD "Zeile 2!

Die Hilfe zu diesem Befehl findet man hier: http://avrhelp.mcselec.com/in-


dex.html?lcd_2.htm

Beispiele
Einfaches "Hallo Welt"-Beispiel
$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 100
$swstack = 100
$framesize = 100

Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , _


Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
Config Lcd = 16 * 2
Cls
Locate 1 , 1
Lcd "Hallo Welt"

End

"Hallo Welt"-Beispiel mit Zähler

Die Erklärungen stehen im Quellcode.

$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 100
$swstack = 100
$framesize = 100

Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , _


Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
Config Lcd = 16 * 2
Cursor Off

Dim Initcounter As Byte


Dim Bt As Byte
Dim S As String * 2

Do
For Initcounter = 1 To 36
'LCD löschen
Cls

'Haupttext der ersten Zeile anzeigen


Locate 1 , 1
Lcd "Hallo Welt...."

'Haupttext der zweiten Zeile anzeigen


Locate 2 , 1
Lcd "Servus LCD...."

'Jede Sekunde um 1 hochzählen und neuen Wert anzeigen


For Bt = 1 To 99
'Neuen Wert in einen Text umwandeln
S = Str(bt)
S = Format(s , "00")

'Neuen Wert in der ersten Zeile ab Spalte 15 anzeigen


Locate 1 , 15
Lcd S

'Neuen Wert in der zweiten Zeile ab Spalte 15 anzeigen


Locate 2 , 15
Lcd S

'Eine Sekunde warten


Wait 1
Next Bt

'Das LCD nach ca. einer Stunden neu initialisieren


If Initcounter = 36 Then
Initlcd
End If
Next Initcounter
Loop

End
Der Haupttext wird jede Minute neu geschrieben. Die Zahl wird jede Sekunde neu
geschrieben. Es wird also nicht jede Sekunde alles neu geschrieben, sondern nur der
sich ändernde Bereich (die Zahl). Würde man jede Sekunde das ganze Display lö-
schen und neu beschreiben, dann würde die Anzeige unangenehm flackern.

Weiterführende Links
 http://www.sprut.de/electronic/lcd/
 http://homepage.hispeed.ch/peterfleury/avr-lcd44780.html
 http://www.ulrichradig.de/site/infos/pdf/LC.pdf
 http://roboternetz.de/wissen/index.php/Bascom_und_LCD%27s
 http://roboternetz.de/wissen/index.php/LCD-Modul_am_AVR
 http://www.geocities.com/dinceraydin/lcd/index.html

Zwei LCDs anschliessen

Kan man mit Bascom auch 2 LCDs ansteuern?

Hallo!

Die Zusatzbibliothek lcd4e2.lib kann zwei LCD-Controller durch Umschalten zwischen


zwei E-Leitungen ansprechen. Diese Bibliothek ist eigentlich dafür gedacht, große
LCDs anzusprechen welche mehr als 80 Zeichen darstellen können. Aber ich denke,
dass man damit auch schlicht ein zweites Display ansprechen kann. Ausprobiert habe
ich es nicht.

Siehe: Roboternetz - LCDs mit mehr als 80 Zeichen

PS: Vielleicht interessanter Link: http://www.roboternetz.de/phpBB2/viewto-


pic.php?p=471872#471872

Analog/Digital-Converter mit dem Befehl GETADC

Langsam ist es an der Zeit mein Versprechen einzulösen, dass ich nach dem LCD-Thema das
Thema Analog/Digital-Converter (ADC) aufgreife. Ein ADC ist so etwas wie ein Spannungs-
messer. Mit einem ADC kann eine Spannung zwischen 0 Volt und der Analog-Versorgungs-
spannung AVCC (normalerweise 5 Volt) gemessen werden. Wenn man eine höhere Spannung
messen will, dann muss man diese zuerst so weit niederbrechen, dass niemals mehr als AVCC
bzw. die angelegte Referenzspannung am zu messenden Pin anliegen kann. Zum Beispiel mit
einem Spannungsteiler aus zwei Widerständen und einer Z-Diode zum Schutz vor Überspan-
nungen. Die Impedanz dieses Spannungsteilers sollte 10 kOhm nicht überschreiten.

Der ATmega8 hat so einen ADC mit an Board. Es ist zwar nur ein ADC eingebaut, aber die-
ser kann von sechs Pins (bei DIP-Gehäuse) oder acht Pins (bei SMD-Gehäusen) angesteuert
werden. Dafür hat der ATmega8 einen so genannten Multiplexer eingebaut. Ein Multiplexer
ist nichts anderes als ein Schalter, der das Signal eines Pins zum ACD umleitet.

Man muss also zuerst den Multiplexer einstellen -- also den Pin auswählen von dem das Sig-
nal zum ADC umgeleitet werden soll. Dann kann man die Messung mit dem ADC beginnen.
Analog to Digital Converter (ATmega8 Datenblatt)

Bascom bietet dafür den Highlevel-Befehl GETADC an. GETADC stellt zuerst den abzufragen-
den Pin über den Multiplexer ein und misst danach die dort angelegte Spannung mit dem
ADC. Du musst dich selber um nichts mehr kümmern.

Du solltest aber auch wissen, dass GETADC kein Wundermittel ist. Wie andere Highlevel-
Befehle auch, hat GETADC auch Nachteile. Einer der Nachteile ist, dass das Programm so
lange still steht (grob geschätzt, bis zu 300 µs), bis die Messung vorbei ist. Der Befehl GE-
TADC stellt zuerst den Multiplexer ein. -- Danach wird erst gemessen. Es könnte also passie-
ren, dass ein Interrupt das Programm genau zwischen dem Umstellen des Multiplexers und
der Messung unterbricht. Das alleine ist noch kein Problem. Es wird nur dann zu einem Prob-
lem, wenn im Interrupt-Handler der Multiplexer auf einen anderen Pin umgeschaltet wird.
Falls du also den Multiplexer innerhalb eines Interrupt-Handlers umstellst (warum auch im-
mer), solltest du vor dem Verwenden von GETADC die Interrupts global ausschalten und da-
nach wieder einschalten (mit den Befehlen DISABLE INTERRUPTS und ENABLE INTERRUPTS ).
Man kann den ADC auch so einstellen, dass Messungen ohne Unterbrechung des Programmes
vorgenommen werden. Das ist kein Problem, aber das funktioniert nicht mit dem Highlevel-
Befehl GETACD. Dafür muss man die Einstellungen direkt in den AVR-Registern setzen
(ohne Highlevel-Befehl).

CONFIG ADC
Mit CONFIG ADC wählt man in erster Linie die Referenzspannung aus. Man kann damit
auch auswählen, wie schnell die Messungen durchgeführt werden sollen. Je schneller, desto
ungenauer. Je langsamer, desto genauer. Aber da das mit dem Parameter PRESCALER =
AUTO automatisch passiert, werde ich nicht weiter darauf eingehen.

Zur Verwendung des ADC mit dem Highlevel-Befehl GETADC, muss man den ADC auf
SINGLE stellen. Da diese Einstellung bei Verwendung von GETADC immer gleich aussieht,
werde ich hierauf auch nicht näher eingehen.

Anders sieht es mit der Auswahl der Referenzspannung aus. Der REFERENCE-Parameter
kann beim ATmega8 auf AREF , AVCC oder INTERNAL eingestellt werden.

Wird AREF als Referenzspannung ausgewählt, dann wird diese direkt vom AREF-Pin ge-
nommen. Diese Referenzspannung muss mit einem 100n Keramikkondensator gegen GND
entstört werden.

Wird AVCC als Referenzspannung ausgewählt, dann wird diese intern an AREF angelegt.
Diese Spannung kann, zusätzlich zur obligatorischen Entstörung des AVCC-Pins, mit einem
100n Keramikkondensator von AREF (nicht AVCC) nach GND entstört werden.

Wird INTERNAL als Referenzspannung ausgewählt, dann wird eine interne 2,56 Volt Refe-
renzspannung an AREF angelegt. Diese Spannung muss mit einem Keramikkondensator ge-
gen GND entstört werden.

Beispiel:

CONFIG ADC = SINGLE, PRESCALER = AUTO, REFERENCE = AVCC

Die Hilfe zu CONFIG ADC findest du hier: http://avrhelp.mcselec.com/index.html?con-


fig_adc.htm

START ADC
Bevor man den ADC verwenden kann, muss man den ADC aktivieren. Und genau das pas-
siert mit dem Befehl START ADC . Beispiel siehe: GETADC
Die Hilfe zu START ADC findest du hier: http://avrhelp.mcselec.com/index.html?start.htm

GETADC
GETADC erwartet als Parameter die ID des zu messenden ADC-Pins. Der ATmega8 stellt sechs,
oder bei SMD-Gehäusen acht, ADC-Eingänge zur Verfügung. Diese sind beim Atmega8 im
DIP-Gehäuse die Pins PC0 bis PC5. Bei SMD-Gehäusen kommen noch die zwei
Pins ADC6 und ADC7 dazu. Diese zwei Pins können nur als ADC-Eingänge verwendet wer-
den. Ansonsten haben diese Pins keine zusätzliche Funktion.

Beispiel:

CONFIG ADC = SINGLE, PRESCALER = AUTO, REFERENCE = AVCC


START ADC
DIM messergebnis AS WORD
messergebnis = GETADC(0)
...
messergebnis = GETADC(7)

Zusätzlich zu den oben genannten Pins (0-7), kann noch eine interne 1,3 Volt Bandgap-Span-
nungsreferenz über die Zahl 14 und GND über die Zahl 15 gemessen werden.

Beispiel:

CONFIG ADC = SINGLE, PRESCALER = AUTO, REFERENCE = AVCC


START ADC
DIM messergebnis AS WORD
messergebnis = GETADC(14) '1,3 Volt Bandgap
messergebnis = GETADC(15) '0 Volt GND

Die Hilfe zu GETADC findest du hier: http://avrhelp.mcselec.com/index.html?getadc.htm

Messauflösung
Die Messauflösung des ADC ist 10 Bit. Das bedeutet, dass maximal 1024 unterschiedliche
Spannungen gemessen werden können. Bei einer Referenzspannung von 5 Volt ist jeder
Messschritt 0,0048 Volt groß. Kleinere Spannungsunterschiede können nicht gemessen wer-
den. Die Genauigkeit schwankt zwischen +-2 Messschritten. Bei 5 Volt Referenzspannung ist
die Genauigkeit also +-0,0097 Volt. Wenn man das grob schätzt, dann kann man sagen, dass
der ADC bei 5 Volt Referenzspannung eine angelegte Spannung auf zwei Kommastellen ge-
nau misst.

Ich habe bis jetzt nur von 5 Volt Referenzspannung geschrieben. Aber man kann jede belie-
bige Spannung zwischen 2 Volt und AVCC als Referenz verwenden. Diese Spannung kann
man an den Pin AREF anlegen. Am Einfachsten ist es, wenn man die Spannung vom Pin
AVCC, also der Spannungsversorgung des analogen Teils des Mikrocontrollers, als Referenz-
spannung verwendet. AVCC als Referenzspannung kann man intern einstellen. Man muss
diese also nicht extra an den Pin AREF anlegen. Um kleinere Spannungen messen zu können,
ohne eine Referenzspannung an AREF anlegen zu müssen, kann man auch eine interne 2,56
Volt Referensspannung auswählen. Mit dem Befehl CONFIG ADC wählt man die Referenzspan-
nung aus.

Da der Mikrocontroller nicht wissen kann, welche Spannung als Referenz anliegt, gibt der
ADC immer nur eine Zahl zwischen 0 und 1024 zurück. 0 bedeutet 0 Volt und 1024 bedeutet
Referenzspannung. Diese Zahl in eine Spannung umzuwandeln liegt an dir.

Die Formel dafür lautet: Vin = Vref / 1024 * ADC

Wenn du eine 5 Volt Referenzspannung verwendest, dann kannst du dir "5.0 / 1024.0" in ei-
ner Konstante speichern. So ersparst du dir diesen Rechenschritt im Programm. Z.B. so:

CONST ADC_MULTI = 0.0048828125 ' = 5.0 / 1024.0

Beispiel:

CONST ADC_MULTI = 0.0048828125 ' = 5.0 / 1024.0


CONFIG ADC = SINGLE, PRESCALER = AUTO, REFERENCE = AVCC
START ADC
DIM messergebnis AS WORD
DIM volt AS SINGLE
messergebnis = GETADC(0)
volt = messergebnis * ADC_MULTI

Entstörung und Anschlussplan


Für analoge Messungen muss der Pin AVCC gut entstört werden. Das geschieht zuerst mal
mit einem 100 nF Keramikkondensator gegen GND. Und zusätzlich sollte man den Strom
über eine 10 µH Spule von VCC beziehen.

Ob nun die interne 2,56 Volt Spannungsreferenz, VCC oder direkt AREF als Spannungsrefe-
renz herangezogen wird, ein 100 nF Keramikkondensator vom Pin AREF nach GND kann
nicht schaden.
Weiters sollte man darauf achten, dass während einer Messung kein Pin des Analogteiles um-
geschaltet wird. Jede Zustandsänderung an einem der Analog-Pins kann das Ergebnis verfäl-
schen. Das ist auch der Grund dafür, dass ich die Analog-Pins nur dann für digitale Operatio-
nen verwende, wenn ansonsten keine anderen Pins mehr zur Verfügung stehen.

Die Pins PC4 (ADC4/SDA) und PC5 (ADC5/SCL) sind Sonderfälle. Da diese Pins auch
für I²C-Verbindungen zuständig sind, werden diese Pins nicht von AVCC sondern von VCC
versorgt. Diese beiden Pins werden also mit der Stromversorgung für den digitalen Bereich
des Mikrocontrollers versorgt. Und nicht wie die restlichen ADC-Pins mit AVCC. Dadurch
sind Messungen an diesen Pins nie so genau, wie an den anderen ADC-Pins. Das sollte man
beim Zeichnen eines Schaltplanes beachten.

Kenndaten des ADC


 Referenzspannung mindestens: 2 Volt
 Widerstand des Referenzspannungs-Pins (AREF): 32 kOhm
 Widerstand am zu messenden Pin (ADC): min 55 MOhm, typisch 100 MOhm
Die genauen Daten findest du im Datenblatt im Kapitel "ADC Characteristics".

Links
 http://www.mikrocontroller.net/articles/AVR-Tutorial:_ADC

Analog-Comparator

Im letzten Beitrag habe ich den Analog/Digital-Converter angesprochen. Das heutige


Thema hat zwar auch mit dem Stichwort "Analog" zu tun, aber nicht mit der Messung
einer Spannung, sondern mit dem Vergleich zweier Spannungen. Der Analog-Com-
parator (AC) vergleicht zwei Spannungen miteinander. Ist die Spannung am Pin
AIN0 (PD6) höher als die Spannung am Pin AIN1 (PD7), dann gibt der Analog-Com-
parator HIGH zurück. Ist die Spannung am Pin AIN0 kleiner als die Spannung am Pin
AIN1, dann gibt der Analog-Comparator LOW zurück.

Das kann man nutzten, um z.B. Pegelstände über ein Potentiometer einzustellen,
welche beim Über- oder Unterschreiten angezeigt werden sollen. Oder um einen Pe-
gel immer unter einem anderen Pegel zu halten. Z.B. zur Ansteuerung einer Wasser-
pumpe, die nur dann Wasser in einen Auffangbehälter pumpt, wenn noch genug
Platz im Auffangbehälter ist.

An welchem Pin (AIN0 oder AIN1) die Referenzspannung angelegt wird ist egal. Die
Spannung darf aber, wie üblich, nicht höher als VCC sein. Statt einer externen Refe-
renzspannung kann eine interne 1,30 Volt (min: 1,15 V; max: 1,40 V) Bandgap-Re-
ferenzspannung verwendet werden. Diese interne Referenzspannung wird intern an
AIN0 angelegt. Der Pin PD6 wird dadurch für andere Anwendungen frei.

Analog Comparator Block Diagram (ATmega8 Datenblatt; Kapitel "Analog Compara-


tor")
STOP AC/START AC
Der AC ist ständig eingeschaltet (aktiv) und muss nicht explizit gestartet werden.
Man kann ihn mit STOP AC ausschalten und mit START AC wieder einschalten.

Die Hilfe zu START AC findest du hier: http://avrhelp.mcselec.com/in-


dex.html?start.htm
Die Hilfe zu STOP AC findest du hier: http://avrhelp.mcselec.com/in-
dex.html?stop.htm

ACSR.ACO
Das ist kein Befehl, sondern die Abkürzung für Analog Comparator Control and
Status Register - Analog Comparator Output. Dieses Bit zeigt den Zustand des
AC an und kann jederzeit abgefragt werden.

Die Hilfe zu diesem Bit findest du im ATmega8-Datenblatt im Kapitel "Analog Compa-


rator".

Beispiel:

$regfile = "m8def.dat"

Led1 Alias Portb.0


Config Led1 = Output

Do
Led1 = ACSR.ACO
Loop

End

Dieses Minimalbeispiel setzt voraus, dass an PB0 eine LED angeschlossen ist. Bei
meinem Test sind an den Pins AIN1 und AIN0 jeweils ein 25k Poti als Spannungstei-
ler (zwischen VCC und GND) angeschlossen. Liegt an AIN0 eine höhere Spannung als
an AIN1, dann leuchtet die LED. Wie man sieht, muss der AC nicht gestartet werden.
Der AC ist ständig aktiv -- außer man schaltet ihn explizit aus (mit STOP AC ).

Interrupt
Der AC kann auch einen Interrupt auslösen. Der AC-Interrupt (ACI) kann bei LOW-
HIGH-Flanke (RISING), bei HIGH-LOW-Flanke (FALLING) und bei Zustandsänderung
(TOGGLE) ausgelöst werden. Dieses Verhalten kann man mit dem Befehl CONFIG
ACI einstellen.

Die Hilfe zu diesem Befehl findest du hier: http://avrhelp.mcselec.com/in-


dex.html?config_aci.htm
Beispiel:

$regfile = "m8def.dat"

Led1 Alias Portb.0


Config Led1 = Output

Led2 Alias Portb.1


Config Led2 = Output

Config Aci = On , Trigger = Toggle


On Aci On_aci
Enable Aci

Enable Interrupts

Do
Led1 = ACSR.ACO
Loop

End

On_aci:
Led2 = Not ACSR.ACO
Return

Dieses Beispiel setzt zusätzlich zum vorherigen Beispiel noch eine LED an PB1 vo-
raus. Wenn man sich mit dem Testaufbau ein wenig spielt, dann fällt auf, dass die
erste LED nach dem Start des Programms sofort entweder ein oder aus ist (je nach
Poti-Einstellung). Die zweite LED wird erst dann ein- oder ausgeschaltet, wenn der
Schwellwert durch Drehen an einem der Potis über- oder unterschritten wird. Der In-
terrupt wird also nur beim "Toggeln" ausgelöst.

Interne Spannungsreferenz
Setzt man das Bit ACSR.ACBG Analog Comparator Bandgap Select, dann wird in-
tern an AIN0 eine 1,30 Volt (min: 1,15 V; max: 1,40 V) Bandgap-Referenzspannung
angelegt. Der Pin PD6 wird damit frei.
Beispiel:

$regfile = "m8def.dat"

Led1 Alias Portb.0


Config Led1 = Output

ACSR.ACBG = 1 ' Interne Bandgap-Referenzspannung verwenden

Do
Led1 = ACSR.ACO
Loop

End

Multiplexer
Der AC kann nicht nur mit den Pins AIN0 und AIN1 arbeiten. Statt AIN1 kann man
den vom Analog/Digital-Converter (ADC) bekannten Multiplexer verwenden. Natür-
lich nur, wenn dieser nicht bereits vom ADC verwendet wird. Damit hat man für den
Analog-Comparator die Pins ADC0 bis ADC5 (bei SMD-Bausteinen auch ADC6 und
ADC7) zur Verfügung. Da es (laut Roland Walter) nach dem Umschalten des Multiple-
xers bis zu 750 ns dauern kann, bis der AC mit der Messung fertig ist, muss man
diese 750 ns warten bevor man das Bit ACSR.ACO auswertet. Wenn der ATmega8
mit 8 Mhz läuft, dann dauert ein Tick 125 ns (1 sec / 8000000 Ticks =
0,000000125). Um den µC 750 ns warten zu lassen braucht es sechs Ticks. Diese
kann man am Einfachsten mit !NOP verbraten. Im Datenblatt finde ich nur den Hin-
weis auf 1 bis 2 Ticks, aber lieber ein paar Ticks zu viel als zu wenig warten. ;-)

Ich verwende im Beispiel das erste Mal den MACRO -Befehl. Damit kann man sich
Schreibarbeit sparen und öfter wiederkehrende Befehle zusammenfassen. Bascom
ersetzt beim Kompilieren des Programms den Makro-Aufruf durch den Inhalt des
Makros. Ein Makro ist schneller als GOTO oder GOSUB und braucht weniger Laufzeit-
ressourcen, aber es wird kein Flash-Speicherplatz gespart.

Beispiel:

$regfile = "m8def.dat"
$crystal = 8000000

Led1 Alias Portb.0


Config Led1 = Output
Led2 Alias Portb.1
Config Led2 = Output

ACSR.ACBG = 1 'Bandgap-Referenzspannung einschalten


SFIOR.ACME = 1 'Multiplexer einschalten

'Makro, welches bei 8 Mhz 750 ns wartet


Macro Wait750ns
!NOP '125 ns
!NOP '125 ns
!NOP '125 ns
!NOP '125 ns
!NOP '125 ns
!NOP '125 ns
End Macro

Do
ADMUX = 0 'nach ADC0 umschalten
Wait750ns 'warten
Led1 = ACSR.ACO

ADMUX = 1 'nach ADC1 umschalten


Wait750ns 'warten
Led2 = ACSR.ACO
Loop

End

In diesem Beispiel wird je eine LED an PB0 und PB1 erwartet. Die in den vorherigen
Beispielen angesprochenen Potis befinden sich für dieses Beispiel an den Pins ADC0
(PC0) und ADC1 (PC1).

Viel Spaß beim Ausprobieren. :-)

Timer/Counter

In diesem Beitrag möchte ich endlich auf die Timer/Counter eingehen. So ein Ti-
mer/Counter ist eine ziemlich nützliche Sache und der ATmega8 hat drei davon. Man
kann damit Ereignisse zählen, genau getaktete Signale erzeugen und noch vieles
mehr. Im weiteren Verlauf dieses und der nächsten Beiträge werde ich nur noch Ti-
mer statt Timer/Counter schreiben. Zur Erklärung: Als Timer zählt er den Systemtakt
und als Counter zählt er externe Ereignisse.

Jetzt wirst du vielleicht fragen, warum man den Systemtakt zählen sollte. Die Sache
ist ganz einfach. Ein Timer kann z.B. nach einer gewissen Anzahl an Ticks einen In-
terrupt auslösen. So hat man einen Interrupt, der immer im gleichen zeitlichen Ab-
stand ausgelöst wird. So kann man einen Interrupt jede Sekunde auslösen lassen.
Und dieser Sekundentakt ist dann so genau wie der an den µC angeschlossene Takt-
geber (Quarz). Wenn man jede halbe Sekunde einen Interrupt auslösen lässt und bei
jedem Interrupt einen Ausgangspin umschaltet (toggelt), dann hat man an diesem
Pin eine Frequenz von genau 1 Hz anliegen. Lässt man den Timer öfter so einen In-
terrupt auslösen, dann wird die Frequenz höher. So können auch hohe Frequenzen
bis in den MHz-Bereich (je nach angeschossenem Quarz) erzeugt werden.

Das Geniale dabei ist, so ein Timer funktioniert unabhängig von unserer Main-Loop.
Der Timer zählt und zählt, egal ob unser Programm gerade voll im Stress ist oder auf
einen Tastendruck wartet. Und das Umschalten von Pins oder das Erzeugen von
"Pulsweiten-Modulation (PWM)"-Signalen passiert im Hintergrund. So kann man ei-
nen Schrittmotor oder einen Servo mit einer genauen Frequenz ansteuern, auch
dann wenn unser Hauptprogramm gerade schläft oder voll im Stress ist.

Nicht jeder Timer kann alles. Timer0 ist der einfachste Timer. Mit ihm kann man ei-
gentlich nur zählen. Timer1 ist der Mercedes unter den Timern. Mit ihm kann man al-
les machen. Timer2 ist ein Mittelding zwischen Timer0 und Timer1. Der hervorste-
chende Vorteil des Timer2 ist, dass er den Takt auch direkt von einem 32,768 kHz-
Uhrenquarz abnehmen kann. Damit kann man mit dem ATmega8 eine ziemlich ge-
nau funktionierende Uhr aufbauen.

Timer0 kann von 0 bis 255 zählen. Timer0 ist ein 8 Bit-Timer. Timer1 kann von 0 bis
65535 zählen (16 Bit-Timer). Und Timer2 kann von 0 bis 255 zählen (8 Bit-Timer).
Den Wert eines Timers kann man jederzeit auslesen und auch überschreiben. Auf
den Wert des Timer0 greift man über das Register TCNT0 zu. Da der Timer1 ein 16
Bit Timer ist, steht dessen Wert in den zwei 8 Bit-Registern TCNT1H und TCNT1L.
Das "H" steht für High und das "L" für Low. Bascom stellt für den einfacheren Zugriff
das Pseudo-Register TCNT1 zur Verfügung. Liest man dieses Pseudoregister aus,
wird ein WORD und kein BYTE zurück gegeben. Der Wert des Timer2 ist über das Re-
gister TCNT2 erreichbar. Als Aliase für TCNT0, TCNT1 und TCNT2 können die Timer-
Namen verwendet werden. Diese sind TIMER0, TIMER1 und TIMER2.

Timing
Ein Counter kann Frequenzen bis zu Systemtakt / 2,5 zählen. Das ist die absolute
Obergrenze. Bei 1 MHz Systemtakt, können Frequenzen bis 400 kHz gezählt werden.
Bei einem Systemtakt von 16 MHz kann ein Counter Frequenzen bis zu 6,4 MHz zäh-
len. Mehr geht nicht, da der Counter eine gewisse Zeit braucht um sich zu synchroni-
sieren, die Signalflanke zu erkennen und den Wert des Zählers zu erhöhen.
Ein HIGH oder ein LOW an einem der Timer-Eingänge muss länger als die Dauer ei-
nes Systemtaktes sein, damit das Ereignis gezählt werden kann. Bei einem Sys-
temtakt von 8 Mhz muss ein HIGH oder ein LOW an einem Timer-Eingang mindes-
tens (1 / 8.000.000) 0,000000125 Sekunden (125 ns) lang sein.

Wer an die Grenzen eines Timers/Counters gehen will, sollte sich auf jeden Fall das
entsprechende Kapitel im ATmega8-Datenblatt durchlesen, um keine unliebsamen
Überaschungen zu erleben.

Overflow-Interrupt
Ist ein Timer beim Zählen am Ende angekommen, dann läuft er über und beginnt
wieder bei 0. Man kann den Timer so einstellen, dass bei so einem Überlauf (Over-
flow) ein Interrupt ausgelöst wird. Die Überlauf-Interrupts der Timer sind OVF0
(OVerFlow0), OVF1 (OVerFlow1) und OVF2 (OVerFlow2). Und wieder sind es die Na-
men der Timer, die man als Alias für die Interrupt-Namen verwenden kann (TIMER0,
TIMER1 und TIMER2).

Prescaler
Man kann einen Prescaler vor einen Timer schalten. Ein Prescaler arbeitet als Vortei-
ler. Er lässt nur jedes X'te Signal durch. Ein Prescaler übernimmt das zu zählende
Signal und gibt erst dann ein Signal weiter, wenn eine einstellbare Anzahl an Ticks
vergangen ist. Der Prescaler dividiert also das zu zählende Signal. Mit so einem
Prescaler kann man das Signal durch 8, 64, 256 und 1024 dividieren, noch bevor es
zum Timer kommt.

Wenn der Prescaler auf 8 eingestellt wird, dann lässt dieser nur jedes achte Signal
durch. Bei einem Systemtakt von 8 MHz und einem Prescaler von 8, kommt am Ti-
mer 1 MHz an. Der Timer bekommt somit weniger zu tun und man kann die ge-
wünschten Zeitabstände genauer einstellen.

Was man noch wissen sollte: Der Prescaler kann nur im Timer-Modus verwendet
werden. Kommt das Signal von extern, also wenn der Timer als Counter läuft, dann
kann kein Prescaler zugeschaltet werden. Dann wird ausnahmslos jedes Signal ge-
zählt. Eine Ausnahme stellt nur der Timer2 dar. Der hat seinen eigenen Prescaler um
einen genauen Sekundentakt ermitteln zu können. Aber darauf werde ich im Beitrag
über den Timer2 genauer eingehen.

Die verschiedenen Timer und deren Möglichkeiten werde ich in den nächsten Beiträ-
gen ansprechen.

Timer0 als Timer (Codebeispiel: Sirene)

Im letzten Beitrag hatte ich dich schon auf Timer eingestimmt. Jetzt geht es an's Ein-
gemachte. :-) Lass' uns doch mal den einfachsten Timer des ATmega8 ansehen -- Ti-
mer0. Dieser Timer kann von 0 bis 255 zählen. Und bei jedem Überlauf (255 -> 0)
kann ein Interrupt (OVF0) ausgelöst werden.
Als "Timer" zählt der Timer0 den Systemtakt. Wenn man keinen Prescaler einstellt,
dann wird bei jedem Tick des Systemtakts auch der Wert des Timers um 1 erhöht.
Würde man 8 als Prescaler einstellen, dann erhöht sich der Wert des Timers nur alle
8 Ticks.

Da der Timer beim 256. Tick wieder nach 0 überläuft, würde der OVF0-Interrupt bei
einem Systemtakt von 8 MHz genau 31.250 mal ausgelöst werden. Würde man im
OVF0-Handler einen Pin toggeln, dann hätte man am Ausgangspin eine Frequenz von
15.625 Hz anliegen. Das wäre eine Frequenz, die man nicht hören würde, wenn man
sie an einem Lautsprecher anlegt.

Würde man das gleiche Spiel mit einem Prescaler von 8 spielen, dann würde der
OVF0-Interrupt bei einem Systemtakt von 8 MHz genau 3.906,25 mal ausgelöst wer-
den. Wie man sieht, ist das keine Ganzzahl mehr. Das liegt an dieser Rechnung:

8.000.000 (Systemtakt) / 8 (Prescaler) = 1.000.000


1.000.000 (Counts) / 256 (Überlauf) = 3906,25

Toggelt man auch in diesem Fall im OVF0-Handler einen Pin, dann würde am Aus-
gangspin eine Frequenz von 1.953,125 Hz. anliegen. Steuert man mit diesem Aus-
gangspin einen Summer (Buzzer) an, dann erhält man einen ziemlich lauten (fast 2
kHz) Ton.

Mach dich bereit, nach dem Übertragen dieses Programmes den Strom vom Board zu
nehmen. :-) Dieser Ton kann ziemlich unangenehm sein (Ohren zuhalten und
Kampfhunde anketten). :-)
$regfile = "M8def.dat"
$crystal = 8000000
$hwstack = 100
$swstack = 100
$framesize = 100

'Buzzer des Atmel Evaluationsboard


'http://www.pollin.de/shop/downloads/D810038B.PDF
Buzzer Alias Portd.7
Config Buzzer = Output

'Timer0 konfigurieren
'8000000 (Systemtakt) / 8 (Prescaler) = 1000000 (Counts)
Config Timer0 = Timer , Prescale = 8

'Timer0 Overflow Interrupt


'Dieser Interrupt wird beim Überlauf (255 nach 0) ausgelöst
'1000000 (Counts) / 256 (8-Bit Timer) = 3906,25 (Interrupts die Sekunde)
On OVF0 On_ovf0
Enable OVF0

Enable Interrupts

Do
!NOP
Loop

End

'Timer0 Overflow Interrupt


'Zwei mal toggeln entspricht einem Impuls. 3906,25 mal toggeln gibt auf dem
'Buzzer einen Ton mit 1953,125 Hertz.
On_ovf0:
Toggle Buzzer
Return

Hier ein etwas angenehmeres Programm: :-)

$regfile = "M8def.dat"
$crystal = 8000000
$hwstack = 100
$swstack = 100
$framesize = 100

'Taster1 auf Atmel Evaluationsboard (mit eigenem Pulldown auf der Platine)
Taster Alias Pind.2
Config Taster = Input

'Buzzer des Atmel Evaluationsboard


'http://www.pollin.de/shop/downloads/D810038B.PDF
Buzzer Alias Portd.7
Config Buzzer = Output

'Timer0 konfigurieren
'8000000 (Systemtakt) / 64 (Prescaler) = 125000 (Counts)
Config Timer0 = Timer , Prescale = 64
'Timer0 Overflow Interrupt
'Dieser Interrupt wird beim Überlauf (255 nach 0) ausgelöst
'125000 (Counts) / 256 (8-Bit Timer) = 488,28125 (Interrupts die Sekunde)
On Timer0 On_timer0_overflow
Enable Timer0

Enable Interrupts

Do
!NOP
Loop

End

'Timer0 Overflow Interrupt


'Wenn der Taster gedrückt wurde, dann wird der Buzzer-Pin getoggelt.
'Zwei mal toggeln entspricht einem Impuls. 488 mal toggeln gibt auf dem Buzzer
'einen Ton mit 244 Hertz aus.
On_timer0_overflow:
If Taster = 1 Then
Toggle Buzzer
Else
Buzzer = 0
End If
Return

Wie man im Beispiel sieht, kann man statt OVF0 auch das Alias TIMER0 verwenden.

Die oben gezeigten Beispiele demonstrierten schon mal das Grundprinzip aller Timer.
Man kann damit zählen und beim Überlauf einen Interrupt auslösen. Man kann den
aktuellen Wert des Timers aber auch auslesen und schreiben. Wenn man den Wert
des Timers setzt, dann zählt der Timer ab diesem Wert nach oben. Je höher man die-
sen Timer0-Wert setzt, desto weniger Zeit vergeht bis zum Überlauf des Timers. Das
kann man nutzen um die Frequenz genauer einzustellen.

Sirene
Das folgende Beispiel demonstriert wie man den Wert des Timer0 bei jedem Überlauf
(im Overflow-Interrupt) neu setzen kann. Das Beispiel soll eine Sirene darstellen.
Dabei wird der Buzzer des Pollin-Evaluationsboards oder des Testboards von Roland
Walter angesteuert. Bitte keinen Lautsprecher direkt anschließen. Das kann den AT-
mega8 zerstören.

Der Vorwahlwert für den Timer0 wird in der Mainloop in zwei For-Schleifen rauf und
runter gezählt. Im Interrupt-Handler wird dieser Wert verwendet um den Timer0-
Wert neu zu setzen. Bis der Interrupt-Handler ausgeführt wird, kann unterschiedlich
viel Zeitvergehen, je nachdem was der µC gerade machen muss. Weiters sichert der
µC vor jedem Interrupt-Handler die AVR-Register. Das benötigt bei jedem Ansprin-
gen des Interrupt-Handlers zusätzliche 66 Ticks. Man kann sich also ausrechnen,
dass der Timer0 in der Zwischenzeit -- wenn kein Prescaler eingestellt wäre -- be-
reits mehr als 66 Ticks hochgezählt hat. Würde man dem neuen Wert des Timer0 die
bereits vergangenen Ticks nicht anrechnen, dann würde die Frequenz des Überlauf-
Interrupts schwanken und zu langsam sein.

Das Setzen des neuen Timer0-Wertes braucht leider auch ein paar Ticks. Diese Ticks
muss man dazuzählen wenn die Frequenz genau sein soll. Das Wort "genau" ist viel-
leicht ein wenig übertrieben. Mit dem Timer0 kann man nur dann eine "sehr genau
einstellbare" Frequenz erzeugen, wenn man den fertigen Maschinencode analysieren
kann um herauszufinden, wie viel Ticks dafür benötigt werden. Da ich es selten so
genau brauche, gehe ich einfach von 9 Ticks aus (so habe ich es in einem Fachbuch
gelesen). Diese Ticks zähle ich gleich beim Auslesen des aktuellen Timer0-Wertes mit
dazu.

Es gilt also: Mit dem Timer0 kann man nur dann eine "sehr genau einstellbare" Fre-
quenz erzeugen, wenn man den fertigen Maschinencode analysieren kann. Wem das
zu umständlich ist, der sollte für "sehr genaue" Frequenzen den Timer1 oder Timer2
verwenden.

$regfile = "M8def.dat"
$crystal = 8000000
$hwstack = 100
$swstack = 100
$framesize = 100

'Buzzer des Atmel Evaluationsboard


Buzzer Alias Portd.7
Config Buzzer = Output

'Timer0 konfigurieren
'8000000 (Systemtakt) / 64 (Prescaler) = 125000 (Counts)
Config Timer0 = Timer , Prescale = 64

'Timer0 Overflow Interrupt


'Dieser Interrupt wird beim Überlauf (255 nach 0) ausgelöst
On Timer0 On_timer0_overflow
Enable Timer0

Enable Interrupts

Dim Timer0_vorwahl As Byte


Dim Current_timervalue As Byte

Do
'schnell hochzählen
For Timer0_vorwahl = 80 To 150 Step 1
Waitms 5
Next Timer0_vorwahl

'den hohen Ton etwas herausziehen


Waitms 1500

'etwas langsamer runterzählen


For Timer0_vorwahl = 150 To 80 Step -1
Waitms 30
Next Timer0_vorwahl
Loop

End

'Timer0 Overflow Interrupt - den Buzzer-Pin toggeln


'
'Zwei mal toggeln entspricht einem Impuls.
'Vorher wird der Timer auf einen Wert eingestellt, ab dem der Timer0
'nach oben zählen soll. Je höher dieser Wert, desto weniger Zeit braucht der
'Timer0 um von 255 auf 0 überzulaufen.
'Damit die in der Zwischenzeit gezählten Ticks in der Rechnung berücksichtigt
werden,
'wird zuerst der aktuelle Timerwert ausgelesen und zum Vorwahlwert dazugezählt.
'Das Setzen des neuen Timer0-Wertes braucht leider auch ein paar Ticks. Es sind
'wahrscheinlich so um die 9 Ticks. Um trotzdem eine ziemlich genaue Frequenz
'zu erzeugen, werden die dafür benötigten 9 Ticks gleich mit dazugezählt.
On_timer0_overflow:
'aktuellen Stand auslesen und 9 Ticks dazuzählen
Current_timervalue = Timer0 + 9
'Timerwert neu setzen
Timer0 = Timer0_vorwahl + Current_timervalue
'Pin toggeln
Toggle Buzzer
Return

Das gleiche Programm läuft auch auf dem ATmega16. Man muss nur die Regfile-
Zeile anpassen: $regfile = "M16def.dat"

Im Schaltplan habe ich statt dem Buzzer einen kleinen 0,5 Watt Lautsprecher ange-
schlossen. Der Lautsprecher hat 8 Ohm. Wenn dein Lautsprecher weniger Widerstand
hat, dann musst du den Vorwiderstand (vor dem Lautsprecher) entsprechend erhö-
hen.

Um die Last des Lautsprechers schalten zu können, habe ich im Schaltplan den Tran-
sistor BC548C eingesetzt. Dieser kann kurzfristig bis zu 500 mA schalten. Der AT-
mega16 wäre zu schwach dafür. Wenn du keinen BC548C hast, dann kannst du auch
den 2N2222 dafür verwenden. Nimm für den 2N2222 aber statt dem 4,7k Wider-
stand einen 1k Widerstand. Sonst ist nicht sichergestellt, dass der Transistor voll
durchschaltet. Und ist das nicht der Fall, dann wird er eventuell heiß.
Viel Vergnügen beim Ausprobieren!

Timer0 als Counter

Im letzten Beitrag habe ich dir den Timer0 als Timer vorgestellt. In diesem Beitrag
geht es darum, den Timer0 als Countereinzusetzen. Als Counter kann man den Ti-
mer0 dazu verwenden, externe Ereignisse zu zählen. Auch hier kann man wieder
nach einem Überlauf den OVF0-Interrupt auslösen lassen. Allerdings kann man,
wenn man den Timer0 als Counter verwendet, keinen Prescaler vorschalten.

Als Eingangspin kann nur der T0-Pin (PD4) verwendet werden. Dafür hat man den
Vorteil, dass man auf Flanken reagieren kann. Man kann auswählen, ob der Timer
bei steigender oder bei fallender Flanke zählen soll.
Üblicherweise zählt man schnelle, digitale Ereignisse und nicht das Drücken eines
Tasters. Das wäre Ressourcenverschwendung. Das Drücken eines Tasters ist so lang-
sam, dass man dafür lieber den Befehl DEBOUNCE verwenden sollte. Der Vorteil des
Counters ist, dass der Zählvorgang den Programmablauf nicht beeinflusst. Noch ein
Vorteil ist, dass man den µC schlafen legen kann, während der Counter im Hinter-
grund unbeeinflusst weiterzählt. Ein OVF0-Interrupt weckt den µC zum Arbeiten kurz
wieder auf und danach kann er wieder schlafen gelegt werden. In so einem Fall ist
das Zählen von "langsamen" Ereignissen mit dem Timer0 natürlich keine Ressour-
cenverschwendung sondern überlegtes Handeln. ;-)

Ich muss zugeben, dass ich mir ziemlich schwer tue, ein gutes Beispiel für den Coun-
ter zu finden. Nehmen wir mal an, ich möchte einen Tastendruck zählen. Dann würde
ich das mit DEBOUNCE erledigen. Und wenn ich einfach zur zählen möchte, wie oft
ein Ereignis auftritt, dann kann ich das über einen Interrupt (INT0 oder INT1) wun-
derbar erledigen. Dafür brauche ich den Counter nicht.

Der Counter wird oft erst dann interessant, wenn sehr schnelle Ereignisse gezählt
werden sollen. Etwas, was das Hauptprogramm ständig unterbrechen bzw. aufhalten
würde. Man kann mit dem Counter Ereignisse zählen, die bis zu Systemtakt /
2,5 schnell sind. Wenn der Systemtakt auf 1 Mhz eingestellt ist, dann kann der
Counter maximal 400.000 Ereignisse die Sekunde zählen. Ereignisse mit höheren
Frequenzen würden verloren gehen. Das Erkennen einer Flanke braucht in etwa 2,5
bis 3,5 Systemticks. Wenn man den ATmega8 voll ausrüstet und mit einem 16 MHz
Quarz versieht, dann können in der Sekunde bis zu 6,4 Millionen Ereignisse gezählt
werden.

In den folgenden Beispielen handelt es sich nicht um Beispiele aus der realen Welt,
sondern um Beispiele die man selber nachbauen und ausprobieren kann. Ich dachte
mir, dass man so den Counter und dessen Möglichkeiten besser verstehen kann.
Denn je besser man die Möglichkeiten des µC kennen lernt, desto vielseitiger lässt er
sich einsetzen. Außerdem habe ich darauf geachtet, auch ein paar Dinge die bereits
gezeigt wurden in den Beispielen zu wiederholen. Und wie immer ist die BASCOM-
Hilfe der erste Anlaufpunkt, wenn man einen Befehl nicht kennt. Man kann die Prob-
lemstellungen der Beispiele auch anders lösen. Aber hier geht es um den Timer0 als
Counter. :-)

Counter0 als Frequenzteiler


Eine recht nützliche Sache ist, dass man nicht auf jedes Ereignis reagieren muss.
Man kann den Counter eine gewisse Menge an Ereignissen zählen lassen und z.B. nur
auf jedes hundertste Ereigniss reagieren. Das entlastet den µC bei hohen Frequenzen
enorm. Als Counter kann man den Timer0, genau so wie als Timer, nach 256 Ticks
überlaufen lassen. Und dabei kann der OVF0-Interrupt ausgelöst werden. Und wenn
man den Timer0 vorher z.B. auf 156 stellt, dann wird der OVF0-Interrupt (alias TI-
MER0) nach exakt 100 Zählereignissen ausgelöst. So kann der Timer0 z.B. zum Tei-
len von Frequenzen verwendet werden. Wenn man die Eingangsfrequenz zum Bei-
spiel durch 100 teilen und an einem Ausgangspin zur Verfügung stellen möchte,
dann wird dafür das Hauptprogramm des Mikrocontrollers nur alle 50 Zählereignisse
unterbrochen um den Ausgangspin zu toggeln. Man kann sich sicher vorstellen, dass
so etwas bei einer Eingangsfrequenz von angenommen 1 Mhz, das Hauptprogramm
merkbar entlastet.

$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 100
$swstack = 100
$framesize = 100

'Diese Konstante gibt an, nach wievielen Zählereignissen der Counter überlaufen
soll.
' Presetvalue = gewünschter Teiler / 2
'Bei 1 würde die Frequenz halbiert werden. Bei 2 würde die Frequenz geviertelt
'werten. Und bei 50 wäre am Ausgang immer ein Hundertstel der Eingangsfrequenz.
' 1/2 = 1
' 1/4 = 2
' 1/6 = 3
' 1/100 = 50
' 1/512 = 256
Const Presetvalue = 1

'Eingang
Config Pind.4 = Input '=T0

'Ausgang
Config Portc.0 = Output

'Timer0/Counter0 einstellen und Überlauf-Interrupt aktivieren


Config Timer0 = Counter , Edge = Rising
On Timer0 On_timer0

Load Timer0 , Presetvalue

Enable Timer0
Enable Interrupts

Do
!nop
Loop

END

'Wenn dieser Interrupt-Handler aufgerufen wird, dann wird zuerst der


'Wert des Timer0 so weit hoch gesetzt, dass der Timer0/Counter0
'erst wieder nach nach <Presetvalue> Zählereignissen ausgelöst wird.
On_timer0:
'Mit dem Befehl LOAD wird der Wert des Timer0 gesetzt.
Load Timer0 , Presetvalue
'Ausgangspin toggeln
Toggle Portc.0
Return

So funktioniert das aber nur, wenn man ein schönes Eingangssignal hat. Damit
meine ich ein Signal, das bei LOW nach GND zieht und bei HIGH nach VCC. Ist das
nicht der Fall, dann kann man mit einem PullUp- oder PullDown-Widerstand am Ein-
gang für klare Verhältnisse sorgen (je schneller das Signal, desto kleiner muss der
Widerstand sein).
Wenn man dieses Beispiel mit einem Taster ausprobieren möchte, dann muss man
bedenken, dass ein Taster prellt. In so einem Fall empfehle ich, den Eingang mit ei-
nem Kondensator zu entprellen, den Taster gegen GND schalten zu lassen und den
eingebauten PullUp-Widerstand des Pins zu aktivieren.

$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 100
$swstack = 100
$framesize = 100

'Diese Konstante gibt an, nach wievielen Zählereignissen der Counter überlaufen
soll.
' Presetvalue = gewünschter Teiler / 2
'Bei 1 würde die Frequenz halbiert werden. Bei 2 würde die Frequenz geviertelt
'werten. Und bei 50 wäre am Ausgang immer ein Hundertstel der Eingangsfrequenz.
' 1/2 = 1
' 1/4 = 2
' 1/6 = 3
' 1/100 = 50
' 1/512 = 256
Const Presetvalue = 1

'Eingang
Config Pind.4 = Input '=T0
Pind.4 = 1 'Pullup-Widerstand aktivieren
'kurz warten, um dem Entprellkondensator des Tasters genug Zeit zum Laden zu
geben
Waitms 1

'Ausgang
Config Portc.0 = Output

'Timer0/Counter0 einstellen und Überlauf-Interrupt aktivieren


Config Timer0 = Counter , Edge = Falling
On Timer0 On_timer0

Load Timer0 , Presetvalue

Enable Timer0
Enable Interrupts

Do
!nop
Loop

END

'Wenn dieser Interrupt-Handler aufgerufen wird, dann wird zuerst der


'Wert des Timer0 so weit hoch gesetzt, dass der Timer0/Counter0
'erst wieder nach nach <Presetvalue> Zählereignissen ausgelöst wird.
On_timer0:
'Mit dem Befehl LOAD wird der Wert des Timer0 gesetzt.
Load Timer0 , Presetvalue
'Ausgangspin toggeln
Toggle Portc.0
Return

Da der Taster nach GND zieht wenn er gedrückt wird, habe ich im Code den Timer0
so eingestellt, dass dieser bei "fallender" Flanke zählt.

Zählen von (schnellen) externen Ereignissen


Abschließend möchte ich noch mit einem Beispiel zeigen, wie der Counter den µC
beim Zählen von schnellen Ereignissen entlasten kann. Damit man es nachvollziehen
kann, bleibe ich bei der Schaltung mit dem Taster (PullUp; Taster nach GND). Um
den gezählten Wert anzeigen zu können, schicke ich ihn über die UART zum Compu-
ter. Und damit man sieht wieviele Ereignisse in den letzten fünf Sekunden gezählt
wurden, wird dieser Wert auch noch ermittelt und in Klammern mit angezeigt.

$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 100
$swstack = 100
$framesize = 100
$baud = 4800

'Diese Konstante gibt an, nach wievielen Zählereignissen der Counter überlaufen
soll.
'Gibt man hier ``1`` an, dann wird jedes Ereignis angezeigt. Gibt man hier
``10`` an,
'dann wird zwar jedes Ereignis gezählt, aber nur jedes 10. Ereignis führt
dazu, dass 'es in den angezeigten Wert (counted_value) übernommen wird.
Const Interval = 10

'Eingang
Config Pind.4 = Input '=T0
Pind.4 = 1 'Pullup-Widerstand aktivieren

'Ausgang
Config Portc.0 = Output

'Zählervariable
Dim Counted_value As Long 'LONG: bis 2147483647
Dim Counted_value_saved As Long
Dim Delta As Long

'Timer0/Counter0 einstellen und Überlauf-Interrupt aktivieren


Config Timer0 = Counter , Edge = Falling
On Timer0 On_timer0

Load Timer0 , Interval

Enable Timer0
Enable Interrupts

Do
'Mit ``DISABLE INTERRUTPS`` wird sichergestellt, dass kein Interrupt-Handler
'den Wert der Variable COUNTED_VALUE verändert, während damit gerechnet
wird.
Disable Interrupts

'herausfinden, wieviele Zählereignisse seit der letzten Berechnung auftraten


'und den neuen Zählstand sichern
Delta = Counted_value - Counted_value_saved
Counted_value_saved = Counted_value

'Der Befehl ``ENABLE INTERRUPTS`` gibt die Interruptroutinen wieder frei.


'Sollte in der Zwischenzeit ein Interrupt ausgelöst worden sein, wird dieser
'Interrupt jetzt nachgeholt.
Enable Interrupts

Print Counted_value_saved ; " (" ; Delta ; ")"

Wait 5
Loop

END

On_timer0:
'Mit dem Befehl LOAD wird der Wert des Timer0 gesetzt, damit der Timer
'nach <Interval> Zählereignissen wieder überläuft.
Load Timer0 , Interval

'Zählervariable um <Interval> erhöhen


Counted_value = Counted_value + Interval
Return

Je höher man den Wert der Konstante Interval einstellt (1-255), desto mehr wird
der µC entlastet.

Und abschließend möchte ich noch betonen, dass es sich hier nur um Beispiele han-
delt, die die Arbeit mit dem Counter verdeutlichen sollen. Natürlich wäre es super,
wenn du die Beispiele selbst nachbaust und nachvollziehen kannst. Die "Best Praxis"-
Lösung gibt es nicht. Jedes Problem kann auf verschiedene Arten gelöst werden. Der
"Timer0 als Counter" ist eine davon.

Speicher HWSTACK SWSTACK FRAME

Der ATmega8 hat 8 kByte FLASH-Speicher (8192 Byte). In diesen Flash wird das Pro-
gramm gespeichert. Dieses Programm bleibt im µC auch wenn man den Strom vom µC
nimmt.

Der ATmega8 hat 1 kByte SRAM (1024 Byte). In diesem SRAM finden die Werte der Pro-
grammvariablen und auch der HWSTACK, der SWSTACK und der FRAME seinen Platz.
Dieser Speicher verliert die Werte wenn kein Strom mehr am µC anliegt.

Der ATmega8 hat 512 Byte EEPROM. Das ist ein nichtflüchtiger Speicher, der beschrieben
werden kann. Das ist vergleichbar mit einer SD-Karte, wie man sie aus den digitalen Fotoapa-
raten kennt. In diesen EEPROM kannst du Einstellungen ablegen, die nach dem Aussschalten
gespeichert bleiben sollen.
Die Größe des Programmes siehst du, nach dem Kompilieren des Programmes, wenn du im
Menü "Programmieren" auf den Menüeitrag "Ergebnis anzeigen" klickst.

Die Größe des Programmes zeigen die Zeilen ROMIMAGE und FLASH USED an.

SRAM
Bascom teilt den SRAM in mehrere Speicherbereiche ein. Einen Teil nimmt der Hardware-
Stack ein. Einen anderen Teil dieses SRAM wird zum Software-Stack und ein anderer Teil
wird unter dem Namen "Frame" reserviert. Diese Speicherbereiche nutzt Bascom direkt um
Rücksprungadressen, Registersicherungen, lokale Variablen und Parameterübergaben an Pro-
zeduren und Funktionen abzuspeichern.

Der Rest des SRAM wird für globale Variablen verwendet.

$hwstack , $swstack und $framesize sollten im Programm angegeben werden. Einfach, damit
man den Überblick über den Speicherverbrauch behält und damit das Programm, falls man es
weitergibt auch bei Anderen läuft. Man kann ja nie wissen, welche Werte der Andere in seiner
IDE eingestellt hat.

Dieser Beitrag wird jetzt ein wenig komplizierter. Wenn du nicht ganz folgen kannst, dann ist
das kein Problem. Ganz unten steht die Empfehlung für die Größe der Speicherbereiche. Zum
Glück hat der ATmega8 genug SRAM-Speicher. So kann man als Anfänger schon mal je 100
Byte für die Stack-Speicher und den Frame reservieren, ohne groß darüber nachdenken zu
müssen. Werden die Programme komplizierter, oder hat man weniger SRAM zur Verfügung,
dann muss man sich über die Speicheraufteilung Gedanken machen. Dann solltest du diesen
Beitrag noch einmal durchlesen und die Einstellungen, deinem Programm entsprechend, an-
passen.

HWSTACK
Der Hardware-Stack ist der Bereich im Speicher, der dafür reserviert ist um Rücksprungadres-
sen und, bei einem Interrupt, eine Sicherung der Register abzulegen.

Springst du mit GOSUB irgendwo hin, dann wird zuvor die Adresse gespeichert, zu der bei
einem RETURN wieder zurück gekehrt werden soll. Wenn du innerhalb dieser Unterroutine
in die du gesprungen bist, noch einmal GOSUB verwendest, dann wird diese zweite Rück-
sprungadresse ebenfalls auf den Hardware-Stack (=Stapel) abgelegt.

Bei einem RETURN wird dann von der zweiten Unterroutine in die erste Unterroutine zurück
gesprungen. Dann wird die Adresse vom Stack gelöscht. Wird die erste Unterroutine beendet
(RETURN), dann springt das Programm zur ersten Rücksprungadresse zurück. Auch diese
Adresse wird wieder vom Hardware-Stack gelöscht.

Wie viel Speicher für diese Springerei benötigt wird, hängt also nicht von der Anzahl an GO-
SUBs oder CALLs ab, sondern davon, wie viele GOSUB oder CALLs ineinander verschach-
telt sind. Für ein GOSUB in eine Unterroutine und noch ein GOSUB innerhalb dieser ersten
Unterroutine und dann noch ein GOSUB innerhalb der zweiten Unterroutine brauchst du 3 x 2
Byte. Wenn du also deine Programme nicht extrem verschachtelst, dann brauchst du selten
mehr als 10 Byte Hardware-Stack für diese Springerei.

Außer dieser Rücksprungadressen, legt Bascom auch die Werte der Register in diesen Stack,
sobald ein Interrupt auftritt und der zugehörige Interrupt-Handler (ISR) angesprungen wird.

Bevor also die Unterprozedur aufgerufen wird, die immer dann automatisch ausgeführt wird,
wenn ein Interrupt auftritt, werden die Register in den HWStack gesichert. Dafür werden 32
Byte benötigt. Wenn du also Interrupts aktivierst, dann brauchst du mindestens 32 Byte
HWStack. Da man in einem Interrupt-Handler so wenig wie möglich macht und die Hauptar-
beit normalerweise in der MainLoop verrichten lässt, ist es unüblich, innerhalb eines Inter-
rupt-Handlers Unterprozeduren anzuspringen. Also wird selten mehr als 32 Byte HWStack
benötigt.

Wenn du es aber doch machst, was ja kein Problem ist, dann musst du zu den benötigten 32
Byte noch die Byte für die Springerei dazuzählen.

Ich würde mal sagen, dass du beim HWSTACK großzügig bist, wenn du diesen am Anfang
auf 40 Byte festlegst. Und falls du weißt, dass du nicht so viel brauchst, weil du z.B. keinen
Interrupt aktiviert hast, kannst du später den HWSTACK entsprechend verkleinern.

$hwstack = 40

SWSTACK
So, und jetzt zum Software-Stack. Gleich wie beim Hardware-Stack gilt, dass nicht die An-
zahl an Unterprozeduren (SUB) dafür ausschlaggebend ist wie groß der Software-Stack sein
muss. Ausschlaggebend ist, wie verschachtelt die Aufrufe untereinander sind.

Wenn du zehn Unterprozeduren hast, aber immer nur eine dieser Unterprozeduren gleichzei-
tig aktiv wird, brauchst du nur so viel Software-Stack-Speicher wie ihn diese eine Unterproze-
dur benötigt. Wenn du innerhalb einer Unterprozedur eine andere Unterprozedur aufrufst,
dann brauchst du im Software-Stack für beide Unterprozeduren Platz. Gleiches gilt auch für
Funktionen.

Wie viel Platz ist das überhaupt?

Du brauchst für jede Variable, die an die Unterprozedur als Parameter übergeben wird, 2 Byte
im SWStack. Hast du also eine Unterprozedur mit 2 Parametern, dann brauchst du dafür 4
Byte SWStack. Rufst du innerhalb dieser Unterprozedur eine andere Unterprozedur auf, die
z.B. 3 Parameter erwartet, dann brauchst du zu den bereits verbrauchten 4 Byte noch (3 x 2) 6
Byte dazu. Das wären dann 10 Byte.

Jede innerhalb einer Unterprozedur mit dem Befehl LOCAL erstellte Variable braucht eben-
falls je 2 Byte im SWStack.
Nehmen wir also an, dass du im Normalfall bis zu 4 Parameter an eine Unterprozedur weiter-
gibst und innerhalb der Prozeduren maximal 3 Variablen mit LOCAL definierst. Weiters neh-
men wir mal an, dass du innerhalb einer solchen Unterprozedur maximal noch eine weitere
Unterprozedur mit wiederum 4 Parametern aufrufst, die auch wieder bis zu 3 lokale Variablen
hat, dann braust du für die Parameter der ersten Prozedur (4 x 2) 8 Byte. Und für die lokalen
Variablen der ersten Prozedur (3 x 2) 6 Byte. Das sind für die erste Prozedur 14 Byte
SWStack. Und die gleiche Menge kommt noch für den verschachtelten Aufruf der zweiten
Unterprozedur dazu. Das wären dann 28 Byte die für den SWStack reserviert werden müsste.

Wenn man noch ein paar Reserverbytes dazu tut, dann bist du in solch einem Fall mit 32 Byte
SWStack gar nicht mal so schlecht dran.

Wenn du also schon am Anfang deines Programmes den SWSTACK auf 32 stellst, dann sind
solche (eher schon komplexe) Vorgänge recht gut abgedeckt. Wenn du später erkennst, dass
du nicht so viel SWSTACK-Speicher benötigst, dann kannst du ihn ja immer noch verklei-
nern.

$swstack = 32

FRAME
Und weiter geht es mit dem FRAME. Im Frame werden Daten und keine Adressen gespei-
chert. Wenn an eine Unterprozedur ein Parameter übergeben wird, dann wird die Adresse die-
ses Parameters im SWSTACK gespeichert. Der Wert, also das was in dieser Variable drinnen
steht, wird im FRAME gespeichert. Allerdings nur, wenn der Parameter mit dem Schlüssel-
wort BYVAL an die Prozedur übergeben wurde. Da es in den meisten fällen besser ist, Werte
mit BYVAL zu übergeben, können wir davon ausgehen, dass der Wert der Variable im Frame
gespeichert wird.

Nehmen wir wieder unsere Unterprozedur -- die mit den vier Parametern. Nehmen wir mal
an, dass der erste und der zweite Parameter je eine BYTE-Variable sind. Der dritte Parameter
ist eine WORD-Variable. Und der vierte Paramter eine STRING-Variable für 10 Zeichen.
Weiters sind in der Unterprozedur 3 lokale BYTE-Variablen definiert.

Wird diese Unterprozedur aufgerufen, dann wird vorher in den HWSTACK die Rücksprunga-
dresse abgelegt. Dann werden im SWSTACK die Adressen der Parameter und der lokalen
Variablen abgelegt. -- jetzt kommts -- Und im FRAME werden die Werte der Parameter und
der lokalen Variablen abgelegt. Wird die Unterprozedur wieder verlassen, dann werden die
Daten im Frame wieder frei gegeben.

2 x BYTE = 2 Byte (erster und zweiter Parameter)


1 x WORD = 2 Byte (dritte Parameter)
1 x STRING * 10 = 11 Byte (inkl. Abschlussbyte; vierter Parameter)
3 x BYTE = 3 Byte (lokale Variablen)

Für diesen Aufruf werden im FRAME also 18 Byte benötigt.


Bascom braucht den FRAME aber auch für andere Sachen. Unter Anderem für die Umwand-
lung von Variablen in andere Datentypen. Du kannst also den FRAME recht groß dimensio-
nieren. Nur wenn dir der übrige Speicher ausgeht, dann musst du den FRAME auf das Nö-
tigste verkleinern.

Wenn du den FRAME großzügig mit 60 Byte dimensionierst, dann sollten die meisten An-
wendungsfälle des ATmega8 abgedeckt sein. Wenn du weißt, dass du weniger brauchst, dann
kannst du den FRAME ja kleiner machen.

$framesize = 60

Vorschläge
ATmega8/ATmega16

Der ATmega8 hat insgesamt 1024 Byte SRAM zur Verfügung. Von diesem SRAM werden
die Bytes für den HWSTACK, den SWSTACK und den FRAME abgezogen.

$hwstack = 40
$swstack = 32
$framesize = 60

Dann bleiben für die Variablen des Hauptprogrammes noch 892 Byte übrig. Man kann also
noch gut nach oben korrigieren, falls mehr HWSTACK, SWSTACK oder FRAME benötigt
wird, oder wenn man auf "Nummer Sicher" gehen möchte.

$hwstack = 100
$swstack = 100
$framesize = 100

Auch mit diesen Werten sollte alles so lange gut laufen, so lange das Programm nicht mehr
als 700 Byte für globale Variablen braucht.

ATtiny13

Der ATTiny13 hat insgesamt nur 64 Byte SRAM. Da muss man schon aufpassen,
keine zu großen Werte für die Stacks und den Frame zu verwenden. Wer den AT-
tiny13 verwendet, muss auf jeden Fall genau wissen, wieviel Speicher für was ver-
wendet wird.

Ich habe z.B. ein Programm auf dem ATtiny13 laufen, das diese Einstellungen ver-
wendet:

$hwstack = 32
$swstack = 5
$framesize = 20
Es bleiben also gerade noch 7 Byte für das restliche Programm übrig.

Wer einen kleinen µC braucht, aber zu wenig SRAM hat, der sollte bei den ATtiny-
Controllern auf den ATtiny45 setzen. Der ATtiny45 hat 256 Byte SRAM.

Je kleiner der SRAM, desto mehr muss man bei der Vergabe der Speicherbereiche
aufpassen. Ab dem ATmega8 ist das nicht mehr so ein großes Problem, da man mehr
Speicher zur Verfügung hat.

Lesestoff:

 http://avrhelp.mcselec.com/index.html?memory_usage.htm
 http://avrhelp.mcselec.com/index.html?stcheck.htm
 http://www.roboternetz.de/wissen/index.php/Bascom_Inside