AVR ATmega - Wie fange ich an - Der Einstieg

dino03

Aktives Mitglied
27. Okt. 2008
6.760
20
38
Sprachen
  1. BascomAVR
  2. Assembler
Hallo an alle Neueinsteiger,

da es einigen Leuten sicherlich nicht leicht fällt, ihre ersten Code-Zeilen für
einen Microcontroller zu schreiben und die dann auch noch auf den
Controller zu übertragen, werde ich einfach mal ein paar Hilfen geben.



===== Die Entwicklungsumgebung für welche die nicht viel ausgeben wollen ;) =====

Was benötigen wir dafür ...

1. Einen PC mit Parallelport (kann also noch ein älteres Exemplar sein)

2. Einen Parallelport-Programmer (siehe http://s-huehn.de/elektronik/avr-prog/avr-prog.htm )
Den mit den 2 Widerständen habe ich auch am Anfang benutzt. Läuft gut.
Die Teile findet man in der Bastlerkiste (altes PC-Kabel und Widerstände)

3. Software ...
- AVR-Studio (gibts bei ATMEL auf der Seite : http://www.atmel.com/ )
- Pony-Prog (gibt es hier : http://www.lancos.com/prog.html )

4. Einen Microcontroller natürlich :) Ein ATmega8-16 ist dafür gut geeignet (ca 1,25eur)
Er ist günstig, überschaubar und mit 16MHz schnell genug für alles mögliche.

5. Ein Steckbrett wie z.B. das auf der Seite von s-huehn weiter unten. Oder auch
etwas größer ... (Das Bild ist mein Steckbrett)
Steckbrett.jpg
So ein Teil kann man später auch sehr gut für Experimente verwenden. Also
kein rausgeschmissenes Geld (zwischen 5,- und 10,-eur)

6. Ein paar Bauteile aus der Bastelkiste, wie z.B. ...
- ein paar LEDs (Low-Current wären gut, es reichen aber auch andere)
- ein paar Vorwiderstände für die LEDs (so zwischen 820 Ohm und 2,2k Ohm)
- Ein paar alte Taster und Schalter für die Eingänge
- noch ein paar Keramik-Kondensatoren (1 oder mehrere 100nF für die Siebung der Betriebsspannung)
- Wenn man einen anderen Takt möchete ein Quarz und 2 Keramik-Kondensatoren mit 22pF. (Es geht aber auch mit internem Takt)

Es muß also nicht teuer sein, um mit einem Microcontroller etwas zu machen.

===== Und weiter gehts =====

Das erste Programm ...

Ich habe damals mit dem LED-Blinker auf der Seite von S-Huehn angefangen.
Aber mir waren einige Sachen darin doch etwas unklar. Also fangen wir unten an :D

Ich nehme mal an, die beiden Programme (AVR-Studio und Pony-Prog) sind installiert.

1. Wir bauen eine Programm-Grundstruktur ...


CodeBox ASM

.include "m8def.inc" ;Definitionsdatei fuer den ATmega8 dazuladen
.cseg ;Beginn eines Code-Segmentes
.org 0 ;Startadresse=0x0000 (Anfang des Flash)

mainloop: ;Ein Sprunglabel

rjmp mainloop ;Schleife neu beginnen (Eine Endlosschleife)

Was sagt uns das nun alles ?

Die Zeile 1 ist eine Assembler-Direktive. Die sagt dem Assembler, er soll die Datei
mit dem Namen "m8def.inc" zu unserem selbstgeschriebenen Programmcode
dazuladen. In dieser Datei sind Definitionen für den ATmega8 enthalten.

In der Zeile 2 teilen wir dem Assembler den Anfang con einem Codesegment mit.
Ab hier beginnt Programm-Code (Auch eine Assembler-Direktive).

Und in der 3. Zeile teilen wir dem Assembler die Anfangsadresse mit, ab der er den
folgenden Programmcode im Speicher des Controllers unterbringen soll. Das ist hier
die erste Adresse des Flash.

Zeile 5 ist ein Label. Diese benannten Punkte kann man vielfältig verwenden.
Entweder als Ziele für Sprünge, als Markierungen für Daten-Tabellen, ...
Der Assembler merkt sich unter diesem Namen die Adresse des Controller-Speichers.
In unserem Fall ist das ein Sprungziel.

In Zeile 7 ist dann ein Sprungbefehl für den Controller. Er soll zur Adresse "mainloop:"
springen.

Wir haben also eine Endlosschleife programmiert die Strom verbraucht :D

===== Wir bringen was zum leuchten =====

Für die nächsten Experimente bietet sich der Port D an, da er alle 8 Bit an
Anschlüsse führt.

Wir schließen also einfach mal an die Pins PD0 bis PD7 Leuchtdioden mit
Vorwiderständen an und verbinden die Anoden mit +5V.
Also z.B. PD0---1,5k---|<|---- +5V

Nun der Code ...


CodeBox ASM

.include "m8def.inc" ;Definitionsdatei fuer den ATmega8 dazuladen
.cseg ;Beginn eines Code-Segmentes
.org 0 ;Startadresse=0x0000 (Anfang des Flash)

ldi r16,0b11111111 ;PortD alle Bits auf Ausgang
out ddrd,r16 ;setzen

clr r16 ;Anfangswert setzen (alles Low)
out portd,r16 ;Daten an PortD ausgeben

mainloop: ;Ein Sprunglabel

rjmp mainloop ;Schleife neu beginnen (Eine Endlosschleife)

Und wieder die Erklärung ...

In Zeile 5 wird das Register 16 (haben wir jetzt mal einfach verwendet) mit dem
Wert 11111111 (Binär) geladen (also 255 dez oder FF hex).

In Zeile 6 wird das I/O-Register DDRD (DataDirectionRegisterD) damit geladen.
Mit einem 1 Bit wird der jeweilige Pin des Ports auf Ausgang geschaltet.

In Zeile 8 löschen wir das Register 16 (also auf 00000000 bin oder 0 dez oder 00 hex)

Und die Zeile 9 schreibt diesen Wert dann in das Ausgangsregister des Port D.
Damit gehen alle Pins des PortD auf Low und die LEDs leuchten, da sie mit ihrem
anderen Anschluß auf +5V liegen.

Es werde Licht :D

===== Es soll aber blinken =====

Also lassen wir es blinken :D

Zählen wir doch einfach mal. Dann blinken alle Bits mit verscheidener Geschwindigkeit.


CodeBox ASM

.include "m8def.inc" ;Definitionsdatei fuer den ATmega8 dazuladen
.cseg ;Beginn eines Code-Segmentes
.org 0 ;Startadresse=0x0000 (Anfang des Flash)

ldi r16,0b11111111 ;PortD alle Bits auf Ausgang
out ddrd,r16 ;setzen

clr r16 ;Anfangswert setzen (alles Low)

mainloop: ;Ein Sprunglabel

out portd,r16 ;Daten an PortD ausgeben
inc r16 ;Datenwert erhoehen

; === hier kommen die naechsten Teile rein

rjmp mainloop ;Schleife neu beginnen (Eine Endlosschleife)

Die Änderungen ...
Die Zeile mit "out portd,r16" ist in die Schleife auf Zeile 12 gewandert.
Man will ja immer den aktuellen Wert des Registers ausgeben und nicht
nur einmal was auf den Port schreiben.

In der Zeile 13 wird das Register 16 jetzt incrementiert (also +1).
Mit jedem Durchlauf wird das Register also um eins hochgezählt.
Das sieht dann so aus ...
0, 1, 2, ... , 254, 255, 0, 1, 2, 3, 4, 5, 6, ... , 253, 254, 255, 0, 1, 2, .....
Das Register läuft also bei 255 über und fängt wieder bei 0 an.
Wir haben allerdings ein Problem. Die LEDs blinken doch sehr schnell.

- out benötigt 1 Takt-Zyklus
- inc auch
- rjmp benötigt 2 Taktzyklen

Das sind dann 4 Taktzyklen für einen Schleifendurchlauf.
Bei 1MHz Prozessortakt ist ein Taktzyklus logischerweise
1/1MHz = 1us
1MHz ist der interne Takt des Mega8 wenn man nicht an den Fuses rumdreht.
Das Register wird also alle 4us um eins erhöht und dann ausgegeben.
256*4us = 1024us = 1,024ms
Damit wird also auf PD7 eine Blinkfrequenz von
1/1,024ms = 977Hz erreicht. Das blinkt aber sehr schnell :D

Die Auflösung des Problems kommt im nächsten Text ... (to be continued)
 
Wir blinken langsamer

Im letzten Text habe ich noch etwas vergessen ...

Folgende Datei von ATMEL ...
doc0856.pdf - AVR Instruction Set (151 pages, revision G, updated 7/08)
Zu finden im Bereich "AVR 8-Bit RISC - Other Documents"
ist beim Programmieren in Assembler sehr hilfreich :D

Jetzt aber die Fortsetzung ...

===== Wir blinken langsamer =====

Um es zu verlangsamen verbraten wir einfach etwas Prozessorzeit.

An der Stelle
; === hier kommen die naechsten Teile rein
fügen wir folgendes ein ...


CodeBox ASM

; Unsere Zeitverbrater-Routine
clr r17 ;Anfangswert setzen (auf 0)
zeit1: ;noch ein Sprunglabel
dec r17 ;Zaehler r17 vermindern (-1)
brne zeit1 ;Zum Label zeit1 springen wenn r17 noch nicht wieder 0 ist

Eigentlich eine einfache Sache ...

In Zeile 2 wird das Register 17 auf 0 gesetzt. (kennen wir schon)

Zeile 3 ist wieder ein Label.

Zeile 4 vermindert diesmal den Wert von r17 (also bei jedem Durchlauf -1)
Die Schleife wird also mit Wert 0 betreten und dann um 1 verringert (auf 255)
und als nächstes getestet.

Zeile 5 ist ein bedingter Sprung (Branch if NotEqual). Springe wenn es nicht
gleich ist. Dieser Befehl testet das Z-Flag (Zero) im Statusregister. Wenn das
Flag nicht gesetzt ist dann springt der Befehl zum Label zeit1 und wenn das
Flag gesetzt ist, macht er mit dem nächsten Befehl weiter.
Das Zero-Flag wird vom Befehl "dec" in Zeile 4 verändert. Wenn er vom Inhalt
des Registers 17 eins abzieht und es kommt 0 heraus, dann wird das Z-Flag
gesetzt. Das wird dann beim nächsten Befehl ausgewertet.

Diese Zeilen bilden also eine Zeitschleife, die 256mal durchlaufen wird.

Also es läuft folgendes ...
zeile2 - r17=0
zeile4 - r17 -1 (=255) Z-Flag wird gelöscht
zeile5 - teste r16 auf 0 => wenn nein dann springen
zeile5 - r17 ist ungleich 0 also springe auf zeit1
zeile4 - r17 -1 (=254) Z-Flag wird gelöscht
zeile5 - teste r17 auf 0 => wenn nein dann springen
zeile5 - r17 ist ungleich 0 also springe auf zeit1
zeile4 - r17 -1 (=253) Z-Flag wird gelöscht
...
..
.
..
...
zeile4 - r17 -1 (=1) Z-Flag wird gelöscht
zeile5 - teste r17 auf 0 => wenn nein dann springen
zeile5 - r17 ist ungleich 0 also springe auf zeit1
zeile4 - r17 -1 (=0) Z-Flag wird gesetzt
zeile5 - teste r17 auf 0 => wenn nein dann springen
zeile5 - r17 ist gleich 0 also nicht springen
jetzt geht es weiter mit den folgenden Befehlen

Wenn wir im Instruction-Set nachsehen, stehen da folgende Takt-Zyklen ...

- clr = 1 Zyklus
- dec = 1 Zyklus
- brne = 1/2 Zyklen (2 beim Sprung und 1 wenn er nicht springt)

das macht dann (1+1+2) * 256 Durchläufe = 1024 Zyklen.
Und weil der letzte Durchlauf nicht springt müssen wir wieder einen
Zyklus abziehen. Also 1023 Zyklen. Das verbraucht also 1,023ms.

Wenn wir jetzt alles zusammenrechen ergibt das ...
1023 Zyklen Zeitschleife + 4 Zyklen Rest = 1027 Zyklen.
macht dann 1,027ms pro Durchlauf.
Wenn wir das jetzt durch 256 teilen (wegen dem höchsten Bit in r16)
ergibt sich für PD7 eine Blinkfrequenz von 3,8Hz bei 1MHz Prozessortakt.
Schon besser :D

===== und jetzt noch langsamer =====



CodeBox ASM

; Unsere Zeitverbrater-Routine mit 16-Bit-Zaehler
clr r17 ;Anfangswert r17 setzen (auf 0)
clr r18 ;Anfangswert r18 setzen (auf 0)
zeit1: ;noch ein Sprunglabel
dec r17 ;Zaehler r17 vermindern (-1)
brne zeit1 ;Zum Label zeit1 springen wenn r17 noch nicht wieder 0 ist
dec r18 ;Zaehler r18 vermindern (-1)
brne zeit1 ;Zum Label zeit1 springen wenn r18 noch nicht wieder 0 ist

Jetzt haben wir zwei ineinanderliegende Zählschleifen. Die Schleife mit r17
liegt in der Zählschleife mit r18. Da die Schleife mit r17 mit 0 als Wert verlassen
wird (wir erinnern uns an das Z-Flag weil Zero = 0 erreicht wurde) können wir
uns das "clr r17" sparen.

Grob gerechnet wurde das gesamte Geblinke also noch einmal um Faktor 256
verlangsamt. Wenn man jetzt immer noch nichts blinken sieht, dann hat man
irgendetwas falsch gemacht oder den falschen Augenarzt :D

Das ist erst einmal der Einstieg. Das ganze Zyklen-Berechnen kann man sich
auch sparen, wenn man nicht gerade zeitkritische Sachen programmiert.
Ich habe es hier nur einmal gezeigt.

Bis jetzt ist alles ohne Stack, Unterprogramm-Aufrufe und Interrupts. Also
eigentlich recht einfach und minimalistisch gehalten. Aber es ist ja
ausbaufähig.

Der nächste Beitrag folgt bestimmt ...

Gruß
Dino
 
Der Stack

So Leute, es geht weiter ...

===== Der Stack =====

Wenn man das Wort übersetzt heißt es eigentlich nur Stapel.
Man hat also einen Stapel, auf den man was drauflegen oder
herunternehmen kann. Bei einem Stapel kommt man nicht an
Sachen, die irgendwo dazwischen liegen. Man kommt also immer
nur an das, was man zuletzt draufgelegt hat. So ein Prinzip
nennt man auch LIFO (Last In First Out).

Im Microcontroller hat man natürlich keinen Blattstapel sondern
einen Stapel im Speicher. Unser Speicher liegt auch nicht irgenndwo
rum sondern ist im Speicher "unter die Decke genagelt" :D
Wir legen also nicht oben drauf sondern kleben unten drunter.

Wie man nun so einen Stack anlegt zeigen die folgenden Zeilen ...


CodeBox ASM

.include "m8def.inc" ;Definitionsdatei fuer den ATmega8 dazuladen
.cseg ;Beginn eines Code-Segmentes
.org 0 ;Startadresse=0x0000 (Anfang des Flash)

ldi r16,low(ramend) ;Unteres Byte aus der vordefinierten Variable RAMEND in r16 laden
ldi r17,high(ramend) ;Oberes Byte aus der vordefinierten Variable RAMEND in r17 laden
out spl,r16 ;Unteres Byte in Stackpointer-Low uebertragen
out sph,r17 ;Oberes Byte in Stackpointer-High uebertragen


Die Zeile 1 bis 3 haben wir ja schon besprochen. Also kommen wir zu 5 bis 8.

In der Datei "m8def.inc" ist eine Umgebungsvariable für den Assembler
definiert die sich RAMEND nennt. Die Zeile sieht folgendermaßen aus ...

.equ RAMEND = 0x045F

Damit kennt der Assembler über die hinzugeladene Definitionsdatei die
RAM-Größe des ATmega8. Das sind 1120 Bytes - was für ne krumme Zahl.
Das sind laut Datenblatt ...
- zuerst die 32 General Purpose Register r0 bis r31
- dann die 64 I/O-Register
- zum Schluß das normale RAM mit 1024 Byte

Da die Zählung mit Byte 0 anfängt enden die 1120 Bytes mit dem
Byte 1119 (0x45F in Hexadezimal).

Die Assembler-Funktionen low und high zerteilen diese 16 Bit lange Zahl
in das untere Byte (low) und das obere Byte (high). Die werden dann
über den Umweg der Register r16 und r17 (direkt geht leider nicht)
in die beiden Bytes des Stackpointers (des Stapelzeigers) geladen.

Wenn wir jetzt ein Byte auf den noch leeren Stack ablegen dann wird
es in der RAM-Zelle 1119 abgelegt und der Stackpointer danach um
eins verringert. Er zeigt danach also auf 1118. (Push)

Wenn wir jetzt dieses Byte wieder vom Stapel holen, dann wird vorher
der Stackpointer um eins erhöht (er zeigt also wieder auf 1119) und
danach wird das Byte von dieser RAM-Zelle abgeholt. (Pop)

Bei einem Sprung in ein Unterprogramm ist es nicht nur ein Byte sondern
der Programmzähler mit seinen 2 oder mehr Byte. Der Stackpointer ist
danach also um die entsprechende Anzahl Speicherstellen nach unten
gerückt. (Call + Ret)

Jetzt nehmen wir unser kleines Blink-Programm und machen aus den
Warteschleifen ein Unterprogramm (Subroutine/Procedure).

Das sieht dann so aus ...


CodeBox ASM

.include "m8def.inc" ;Definitionsdatei fuer den ATmega8 dazuladen
.cseg ;Beginn eines Code-Segmentes
.org 0 ;Startadresse=0x0000 (Anfang des Flash)

ldi r16,low(ramend) ;Unteres Byte aus der vordefinierten Variable RAMEND in r16 laden
ldi r17,high(ramend) ;Oberes Byte aus der vordefinierten Variable RAMEND in r17 laden
out spl,r16 ;Unteres Byte in Stackpointer-Low uebertragen
out sph,r17 ;Oberes Byte in Stackpointer-High uebertragen


ldi r16,0b11111111 ;PortD alle Bits auf Ausgang
out ddrd,r16 ;setzen

clr r16 ;Anfangswert setzen (alles Low)

mainloop: ;Ein Sprunglabel

out portd,r16 ;Daten an PortD ausgeben
inc r16 ;Datenwert erhoehen

rcall warten ;Hier wird das Unterprogramm aufgerufen

rjmp mainloop ;Schleife neu beginnen (Eine Endlosschleife)


; ===== Hier endet das Hauptprogramm und es fangen die Unterprogramme an

; Unsere Zeitverbrater-Routine mit 16-Bit-Zaehler
warten: ;Die Einsprungmarke fuer unser Unterprogramm
clr r17 ;Anfangswert r17 setzen (auf 0)
clr r18 ;Anfangswert r18 setzen (auf 0)
zeit1: ;noch ein Sprunglabel
dec r17 ;Zaehler r17 vermindern (-1)
brne zeit1 ;Zum Label zeit1 springen wenn r17 noch nicht wieder 0 ist
dec r18 ;Zaehler r18 vermindern (-1)
brne zeit1 ;Zum Label zeit1 springen wenn r18 noch nicht wieder 0 ist
ret ;Wir springen zurueck in das Hauptprogramm



Nun haben wir die Zeitroutine aus dem Hauptptogramm herausgelöst und als
Unterprogramm aufgerufen. Man kann es jetzt auch aus anderen Stellen des
Hauptptogramms aufrufen wenn man dort die entsprechende Verzögerung
benötigt. Das spart Arbeit und Speicherplatz.

Der Befehl rcall hat gegenüber call nur den Unterschied das er einen relativen
Sprung ausführt und dadurch etwas Speicherplatz und Zeit spart.

rcall - Relative Call (relativer Sprung zum Unterprogramm)
man gibt also die positive oder negative Distanz zum Unterprogramm an.

call - Einfach nur Call (direkter Sprung zum Unterprogramm)
man gibt die absolute Adresse des Unterprogramms an.

Wer es genau wissen will, lese in doc0856.pdf "Instruction Set" nach.
Das wird beim programmieren sowieso öfters notwendig werden.

Soviel erst einmal zum Stack und Unterprogrammen.

Weiterhin viel Spaß wünscht
Dino
 
Hallo Dino,

klasse - obwohl ich für eine ganze Weile erst einmal BASCOM benutzen werde. Meine eigentlich nicht vorhandene Asembler-Erfahrung liegt so ca. 20jahre zurück. Aber wer weiss.....


Grüsse,

Michael
 
Pushen und Poppen

Hi Michael,
danke fürs Lob :)

Und weiter geht es mit dem Stack ...

===== Pushen und Poppen auf dem Stack =====

Den Stack und Unterprogramme kennen wir jetzt schon. Jetzt legen wir aber mal
selber Daten auf den Stack.

Warum sollte man das machen ?
- um Daten vor dem Überschreiben zu retten
- um Platz in den Registern zu schaffen
- um sich die Arbeit an manchen Stellen zu vereinfachen

Wie das geht zeige ich jetzt mal am Beispiel unseres kleinen Unterprogrammes ...


CodeBox ASM

; Unsere Zeitverbrater-Routine mit 16-Bit-Zaehler
warten: ;Die Einsprungmarke fuer unser Unterprogramm

push r17 ;r17 auf Stack retten (fuer wiederherstellung)
push r18 ;r18 auf Stack retten (fuer wiederherstellung)

clr r17 ;Anfangswert r17 setzen (auf 0)
clr r18 ;Anfangswert r18 setzen (auf 0)
zeit1: ;noch ein Sprunglabel
dec r17 ;Zaehler r17 vermindern (-1)
brne zeit1 ;Zum Label zeit1 springen wenn r17 noch nicht wieder 0 ist
dec r18 ;Zaehler r18 vermindern (-1)
brne zeit1 ;Zum Label zeit1 springen wenn r18 noch nicht wieder 0 ist

pop r18 ;r18 vom Stack holen und wieder herstellen
pop r17 ;r17 vom Stack holen und wieder herstellen

ret ;Wir springen zurueck in das Hauptprogramm


In den Zeilen 4 und 5 werden die Inhalte der Register r17 und r18 auf den Stack
geschoben. Der Inhalt der Register bleibt dabei erhalten.

In den Zeilen 15 und 16 werden die geretteten Inhalte der Register wieder vom
Stack zurückgeholt und überschreiben die aktuellen Registerinhalte.

Man beachte dabei die Reihenfolge der Aktionen! Es gilt immer noch das
LIFO-Prinzip. Was man zuletzt auf den Stapel legt muß man zuerst wieder
herunter nehmen. Mit dieser Aktion haben wir den Inhalt der beiden Register gesichert
und können sie im Unterprogramm gefahrlos für andere Zwecke verwenden.
So wie in unserer Warte-Routine z.B. für die beiden Zeitschleifen-Zähler.

Als Beispiel für Arbeitserleichterung kann man z.B. folgendes nehmen ...


CodeBox ASM

; Wir vertauschen 2 Registerinhalte ohne ein anderes Hilfsregister
push r21 ;r21 auf Stack schieben
mov r21,r20 ;Inhalt von r20 in r21 laden
pop r20 ;Daten vom Stack in r20 holen


Wir benutzen also den Stack als Zwischenlager um den Inhalt der Register
r20 und r21 zu vertauschen. Wenn man wie üblich mal wieder zu wenig
Register zur Verfügung hat, ist die Methode am elegantesten.

Wie man sieht kann man mit dem Stack viele kleine programmtechnische
Ferkeleien anstellen. Man muß nur auf die Reihenfolge der abgelegten
Daten und Sprungziele achten. Wenn man z.B. in einem Unterprogramm
mehr vom Stack holt als man draufgetan hat, vernichtet man seine
Rücksprungadresse. Man kann auf diesem Weg aber dem Unterprogramm
auch eine andere Rücksprungadresse unterschieben um z.B. im Fehlerfall
nicht in das Hauptprogramm sondern in eine Routine zur Fehlerbehandlung
zu springen.

Denn mal viel Spaß beim Speedstacking mit 16MHz :D

Weitermachen werde ich wohl mit den Fuses als erst einmal letzten Beitrag.
Weitere Beiträge werden dann wohl in loser Folge nach und nach entstehen
wenn mal wieder Zeit ist.

Gruß
Dino
 
Fuses (gefährliche Sicherungen)

===== Fuses (gefährliche Sicherungen) =====

Jetzt kommen wir zu einem gefährlichen Thema. Wenn man hier etwas verdreht
und nicht genau weiß was man tut, dann kann man seinen Microcontroller nicht
mehr ansprechen. Außer man hat ein Board für Parallel-Programmierung (z.B. STK500)

In meinen Programmen baue ich am Anfang immer Informationen über das System
ein, was ich programmiere und wie die Hardware aussieht und alles mögliche andere.
So z.B. auch die Fuses der ATmegas. Sie sind von Typ zu Typ unterschiedlich in der
Anzahl. Sie gehören genauso dazu wie das Programm selber. Was nutzt einem z.B.
ein Bootloader wenn man nachher nicht mehr weiß, wie groß man den Bootbereich
eigentlich eingestellt hat. Also gewöhnt euch gleich mal an, vorne im Sourcecode
die Einstellung der Controller-Fuses unterzubringen.

Das kann dann z.B. so aussehen ...
Code:
; ==============================
; ========== ATmega32 ==========
; ==============================
;
; ----- FUSES -----
;
; * SUT1 und SUT0 (Zustand=11): Start-up Time 65ms nach Reset,
;   Einstellung fuer Quarzoszillator und langsam ansteigende 
;   Betriebsspannung (Tabelle 5 des Datenblattes)
; * CKSEL3-CKSEL0 (Zustand=1111): Quarzoszillator im Bereich 3-8MHz 
;   (Tabelle 4 des Datenblattes)
; * CKOPT (Zustand=1): schneller Quarzoszillator (Tabelle 4 des Datenblattes)
; * BODEN (Zustand=0): Brown-out einschalten
; * BODLEVEL (Zustand=1): Brown-out Schwelle auf 2,7V setzen
;
;  Unter Beachtung der invertierten Logik der Fuse-Bits sollte man 
;  also die Fuses so setzen wie im folgenden Bild: 
; 
; (   )7 (   )6 [   ]BootLock12 [   ]BootLock11 [   ]BootLock02 [   ]BootLock01 [   ]Lock2 [   ]Lock1
;
; [   ]OCDEN [   ]JTAGEN (X)SPIEN [   ]CKOPT  [   ]EESAVE [X]BOOTSZ1 [X]BOOTSZ0 [   ]BOOTRST
;
; [   ]BODLEVEL [X]BODEN [   ]SUT1 [   ]SUT0 [   ]CKSEL3 [   ]CKSEL2 [   ]CKSEL1 [   ]CKSEL0
;  ______________________
; |                                            |
; | [X] Bit=0       [   ] Bit=1         | (   ) -> Nicht anwaehlbar  [   ] -> Anwaehlbar
; | progr.          unprogr.          |
; |______________________|
;

Ich habe eigentlich nur das Fenster des PonyProg in ASCII umgesetzt.

Was man zum Anfang beherzigen sollte ...

- 1. Laßt die Finger von den Lock-Bits ! Die verhindern Programmierung und Auslesen

- 2. Schaltet nicht mit 0000 für CKSEL0-3 auf "external Clock" sonst braucht ihr zu
Wiederbelebung einen externen Oszillator (ich meine damit nicht nur ein Quarz sondern
eine richtige Taktquelle)

- 3. Deaktiviert das JTAGEN-Bit. Es könnte später stören.

- 4. Laßt die Finger von der Fuse "RSTDISBL" !!! GANZ WICHTIG !!!
Sie ist nicht bei allen ATmegas vorhanden. Meist aber bei den kleineren Typen.
Im Datenblatt des ATmega8 steht darüber folgendes ...
Alternate Functions of Port C

• RESET – Port C, Bit 6
RESET, Reset pin: When the RSTDISBL Fuse is programmed, this pin functions as a normal I/O
pin, and the part will have to rely on Power-on Reset and Brown-out Reset as its reset sources.
When the RSTDISBL Fuse is unprogrammed, the reset circuitry is connected to the pin, and the
pin can not be used as an I/O pin.
If PC6 is used as a reset pin, DDC6, PORTC6 and PINC6 will all read 0.
Wenn man an der Fuse rumfummelt, dann kann man danach den Controller nicht
mehr über SPI programmieren. Entweder man hat für diesen Fall ein Board was als
Parallel-Programmer arbeiten kann (wie den STK500) oder man hat einen Mülleimer.
Für die Programmierung des Controllers über SPI wird zwingend der RESET-Pin
benötigt. Um sich Arbeit zu sparen sollte man also lieber auf einen ATmega mit
mehr Pins ausweichen wenn man zu wenige IO-Ports hat. Sonst kann man ihn nur
ein einziges mal "Im System" programmieren und danach nie wieder.


Das waren erst einmal die wichtigsten Sachen über die Fuses. Als Tip kann ich euch
nur mitteilen : Seht euch die Einstellungen, die ihr da machen wollt vorher im
Datenblatt des jeweiligen Controllers an. Jeder Typ hat hier seine Eigenheiten und
mehr oder weniger Einstellmöglichkeiten.

Wen man nicht ganz sicher ist, was man da einstellen soll ...
Hier im Forum wird einem mit Sicherheit weitergeholfen. Es ist besser vorher zu fragen
als nachher den Controller in den Müll schmeißen zu müssen :D


===== Schlußwort =====

Das war erst einmal der letzte Beitrag meiner Einführung. Ich hoffe mal, ich helfe
damit einigen Einsteigern weiter. Ich kann nur empfehlen sich am Anfang nicht zu
große Projekte und Aufgaben zu stellen. Man ist sonst schnell frustriert weil etwas
anscheinend überhaupt nicht funktionieren will. Nehmt euch ein kleines Beispielprogramm
und baut von diesem Punkt ausgehend euer Programm auf. Rom wurde auch nicht an
einem Tag gebaut ;)

Mit welcher Programmiersprache ihr arbeiten wollt ist eigentlich erst einmal egal.
Hauptsache ihr kommt damit zurecht und könnt eure Probleme lösen. Scheut euch
auch nicht, Teile von anderen Programmen zu verwenden. Warum soll man das Rad
denn neu erfinden. Ich kupfer auch von anderen ab und passe die Teile dann an
meine Programme an. Am Anfang kann man die genaue Funktion einzelner
Sachen noch nicht bis ins Detail verstehen. Es reicht aber erst einmal aus, wenn man
weiß wie man die Daten an diese gekupferten Routinen übergeben kann und wie man
sie zurückbekommt (die Softwareschnittstelle also). Nach und nach wird man dann
auch diese Teile verstehen und besser an seine eigenen Bedürfnisse anpassen können.

Eine kleine Hilfestellung zur Programmiersprache :

- Mit Assembler kann man sehr hardwarenah programmieren. Man ist sozusagen per-Du
mit jedem einzelnen Bit und kann der CPU genau sagen, was sie tun soll. Bei
Berechnungen wird es hier aber schnell kompliziert. Man kann nicht einfach mal zwei
Zahlen mit Kommastellen addieren oder multiplizieren. Aber für manche Sachen
gibt es keine Alternative.

- Mit Basic (BASCOM) kann man sehr schnell zu Ergebnissen kommen wenn man
sich noch nicht so auskennt. Für Hardware muß aber eine entsprechende Bibliothek
vorhanden sein (z.B. LCD, 1-Wire, ...) oder man muß es wie bei Assembler doch wieder
selber machen. Der Compiler hängt immer zwischen dem Programmierer und dem
Prozessor. Es kann also immer einmal etwas nicht so funktionieren wie man will, weil
der Compiler anders denkt als man selbst.

- Mit C ist es eigentlich ähnlich wie mit Basic. Es ist allerdings eher für etwas
fortgeschrittenere Programmierer gedacht. Im großen und ganzen gelten aber die
Sachen, die ich unter Basic schon gesagt habe.

Die Programmiersprache ist teilweise auch ein wenig Lebensphilosophie ;)
So wie es die einzelnen Lager Sinclair/Commodore , Atari/Amiga oder Windows/Linux
gibt, so gibt es auch die Lager für die einzelnen Programmiersprachen.
Jede hat irgendwie ihre Vor- und Nachteile. Ich halte mich da zurück.
Das schöne ist allerdings, man kann Assemblerteile in den Quellcode der anderen
Sprachen einfügen und damit zeitkritische oder hardwarenahe Teile anlegen. Im
Endeffekt landet man also doch wieder beim Urschleim :D

Das war es nun aber auch. Jetzt warte ich mal auf eure Verbesserungen, Korrekturen
und sonstigen Kommentare zu meiner kleinen Einführung ...

Viel Spaß beim tippen wünscht ...
Dino
 
Hallo Dino,

schöne und kurzweilige Erläuterungen zum Thema "AVR - Wie fange ich an". :) Als ich mich das erste mal mit AVR-Mikrocontrollern befasst habe, hätte ich die gut gebrauchen können :rolleyes:

Danke für den Beitrag!

Grüße,
Dirk
:ciao:
 
Mein Parallelport-Progger (STK200-kompatibel)

Hallo,

hier für die Einsteiger der Schaltplan meines zweiten Proggers. Der erste war die
2-Widerstände-Version :D

STK200-ParProgger.png

Detaillierte Bilder des Progger ...

P1030459.JPG P1030460.JPG

P1030461.JPG P1030462.JPG

Viel Spaß beim Basteln ...

Gruß
Dino
 
LPT-Progger (STK200) in der Minimalversion

Hallo zusammen,

ich hab meinen ersten Progger wiedergefunden :D und da möchte ich euch
natürlich das Bild nicht vorenthalten ...

Das ist er ;)
P1050212.JPG
Die Adernfarben ...
blau - GND
grün - MISO
rot - MOSI
gelb - SCK
weiß - RESET

Der Schaltplan ist auf der Seite von Scott-Falk Hühn zu finden ...
AVR-Programmierung (alte Seite)
Für Leute die anfangen aber sich nicht gleich nen USB-Progger kaufen
möchten ist das eigentlich die beste Lösung (wenn der PC noch nen richtigen
LPT-Port hat). Auf der neuen Seite ...
AVR-Programmierung
ist mittlerweile ein USB-Progger abgebildet. Also entweder nen fertigen
Progger kaufen oder mal wieder das Henne-Ei-Problem haben. Wenn man
sich nen USB-Progger selber bauen will um den Atmel damit zu programmieren
muß man zuerst den Atmel auf dem Progger programmieren. Ääähhh ... Mist ! :eek:
Also ist die günstigste Lösung immer noch nen Steckbrett. Dann einen Atmel
kaufen, den LPT-Progger selberbauen und die Elektronik-Restekiste plündern
um ein paar Widerstände und Kondensatoren für den ersten Aufbau zu haben. :D

Gruß
Dino

:eek: SCHHHHEEIIIISSSS UMTS-Zwangsproxy !!! Meine Buttons reagieren teilweise nicht !!!! :mad:
Verbindung neu aufgebaut und es geht auf einmal wieder ... Dreck ! ... OK ist Off-Topic
 
Hi Dino
Hätt ich mal doch ein wenig mehr gestöbert, dann wär mir dein Beitrag zum Einstieg mit Assembler vielleicht nicht entgangen. Ok, dann werd ich mal mein Werk erst mal zuende bringen...
Gruß oldmax
 
Hallo Dino,

da ich nun schon einige Projekte mit ATMEL´s gebaut habe, aber noch keine Ahnung vom Erstellen des Programmcodes habe, möchte ich mich mit Deiner Anleitung an diese Problematik wagen. Die Hardware, die man dazu benötigt, wie den LPT-Progger, USB-ISP-Progger und das Pollinboard, habe ich mir schon gebaut. Ich bin mal gespannt, was mir da gelingen wird.

Danke für die schöne Anleitung!

Gruß, Hubi47
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)