Wie ziehe ich ein Projekt durch ... (7-Segment-Multiplexanzeige)

dino03

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

mir steht mal wieder der Sinn nach einer Erklärung/Anleitung :D
Da anscheinend viele Probleme mit der Durchführung von Projekten haben,
will ich mal eine kleine FAQ dafür schreiben. Wie fängt man ein Projekt
an, wie plant man und wie führt man es durch. Alles natürlich so, wie ich es
machen würde. Es bleibt jedem selbst überlassen, es anders zu machen.

Als erstes: Was soll es werden? Ich hab vor, im Endeffekt eine
7-Segment-Anzeige mit 2 Tastern zu basteln, die man hoch- und
runtertasten kann. Es soll ein 8-Bit-Wert geändert und angezeigt
werden. Der Einfachheit halber soll die Anzeige in HEX geschehen.

Als nächstes machen wir uns Gedanken über die Hardware. 8-Bit in HEX
sind 2 Stellen. Für die Segmente benötigen wir minimal 7 Pins, für die
2 Stellen 2 Pins. Die beiden Taster brauchen auch noch 2 Pins.
Gesamtzahl also mindestens 7+2+2 = 11 Pins. Damit man einfacher
programmieren kann sollte man die zusammengehörigen Funktionen
auf dem selben Port sammeln. Also Ein Port mit 7 Pins für die Segmente,
ein Port mit 2 Pins für die Stellen und ein Port mit 2 Pins für die Taster.

Jetzt sucht man die Anschlußbelegungen der ATmegas durch und sucht
nach Typen, die einem da gefallen. Der ATmega8 würde für diese
Aufgabe reichen. Auf PortB liegen die Pins, die für den ISP-Anschluß
benötigt werden. PortD hat PD0..7 zur Verfügung = 8 Pins. Das paßt für
die Segmente. PortC wird jetzt einfach für die Stellen verwendet und
PB0 und PB1 von PortB werden nicht für die Programmierung verwendet.
Also packen wir da die beiden Taster dran. Damit ist die grundlegende
Planung erst mal abgeschlossen.

Jetzt setzen wir das alles in einen Schaltplan um, damit man es zum
zusammenstecken vor Augen hat und weiß was man sich da
zusammengeplant hat.

(Fortsetzung folgt ... :D )
 
Der erste Schaltplan (die Grundschaltung)

und weiter gehts ...

Die erste Schaltung mit den wichtigsten Bauteilen sieht man im folgenden Bild ...
Projekt_1.gif

Ich hab ein externes Quarz mit 16MHz und den dazu gehörenden Kondensatoren
mit 22pF angeschaltet. Die Betriebsspannungsanschlüsse VCC und AVCC haben
die benötigten Sieb-/Abblock-Kondensatoren. Der AREF besitzt für die Siebung
auch einen Kondensator. Am RESET-Eingang habe ich zusätzlich zur inneren
Schaltung noch einen Pullup-Widerstand und einen Kondensator angebaut.
Außerdem ist ein Stecker für den ISP-Anschluß zum programmieren vorhanden.
Die Induktivität, die von Atmel für die gefilterte Versorgung des Analog-Teils
(AVCC AD-Wandler-Versorgung) vorgeschlagen wird, habe ich der Einfachheit
halber durch einen 47 Ohm Widerstand ersetzt.

Damit ist der ATmega8 lebensfähig und könnte das erste mal an Saft und den
Programmer angeschlossen werden. Sehen kann man allerdings noch nichts.

(Fortsetzung folgt ... :D )
 
Zuletzt bearbeitet:
Hi Thomas,

der Quarz mit Beschaltung ist nicht nötig (Minimalistenlösung :D ) und den R2 würde ich durch eine Ferritperle ersetzen.
Das ist dann aber die Mini-Micro-Minimallösung :D :D
Kann man machen, aber ich wollte den Schaltplan für Anfänger gleich etwas
universeller gestalten. Dann können die auch ein wenig Analogkram machen.

Gruß
Dino
 
Hi,

ist schon OK. Wollte nur darauf hinweisen. Sogar den R2 oder die Ferritperle kann man unter Umständen durch eine Drahtbrücke ersetzen :D

Wenn Du meine Projekte kennst, wirst Du sehen, das ich immer Minimalistenlösungen anstrebe. Mein Motto : So viel wie möglich mit so wenig wie möglich. Ehrlich gesagt, hat mir das schon oft weitergeholfen. Was nicht da ist, kann auch keine Fehler machen :to_pick_ones_nose:

Thomas
 
Hi Thomas,

hör auf in Kleinigkeiten zu wühlen. Lass Dino ruhig seine Ausführungen zum Projektablauf fertigmachen. Ist sicherlich ganz hilfreich strukturierte Vorgehensweisen mal zu diskutieren und zu besprechen.

Ich möchte aber Deinen Tatendrang nicht beenden und habe dafür, speziell für Dich eine eigene Hausaufgabe.
Mach doch einen eigenen Thread auf in dem Du simple Basisbeschaltungen zeigst:

- Wie sieht eine simple Basisbeschaltung des Mega aus?
- Wie macht man eine simple Tasteneingabe
- Wie steuert man ein Relais
- Basisbeschaltung für I2C und 1Wire
- Basisbeschaltung mit und ohne ADC
- Standardtreiberstufen
...
...

Vielleicht bekommen wir so eine nützliche Bibliothek mit Standard-Dingen zusammen die vielen Usern eine nützliche Hilfe sein wird. Was hältst Du davon?

Grüße,
Markus
 
Hallo Markus,

das Problem ist, das viele sich viel zu sehr an solche Basisschaltungen halten und somit den Wald vor lauter Bäumen nicht finden. Ich kann Dir da einige Beispiele nennen.

Ich finde es prima, was Dino da macht, aber ich würde es ehrlich gesagt nicht tun. Deshalb werde ich auch meine Hausaufgaben nicht machen :aetsch: Nein, ich mache die Arbeit von Dino nicht madig, er darf gerne weiter machen und soll es auch.

Es gibt viel zu viele unterschiedliche Fälle, als das man sie verallgemeinern könnte und sie in eine Schablone packen kann. Ich bin jetzt noch einmal kleinlich : Die 5V Versorgungsspannung. Erinner Dich mal ein den allerersten Astro-Timer, der nicht nur von mir kommt. Es vergingen einige Tage, bis alle davon überzeugt waren, das man nicht unbedingt 5,00V mit Spannungsregler und dem ganzen PiPaPo braucht. Ich hoffe, Pit nimmts mir nicht übel ;) Hättest mal die "Versorgungslösungen" sehen sollen. Vom 9V-Block bis zum Netzgerät war alles vorhanden.

Ich halte nicht viel von vorgefertigten Schablonen. Als Lehrbeispiel, zum Probieren und zur Hilfestellung OK, aber nicht um sie blindlings zu kopieren :flirt:

Thomas
 
Die 7-Segment-Anzeigen

... jetzt wirds bunt ... :D

Jetzt sind die 7-Segment-Anzeigen dran. Zuerst sehen wir uns mal zwei kleine
Ausschnitte aus einem Datenblatt an ...
7Seg_1.gif
Das sind die Grenzwerte von einem 7-segment-Display.
Da steht drin, das die roten Displays maximal 500mA Strom pro Segment für
sehr sehr kurze Zeit vertragen können. Bei den orangen und grünen sind es
sogar nur 150mA. Das sind Pulszeiten im Millisekundenbereich! Längere Impulse
lassen die LEDs in der Anzeige zuverlässig verdampfen :D
Maximal vertragen die Displays 30mA bzw 20mA bei 75Grad Sperrschicht-Temperatur
der LEDs. Außerdem darf man maximal 6V in Sperrrichtung anlegen. Danach
ist die Anzeige Schrott wenn man den Strom in Sperrrichtung nicht stark
begrenzt.

Jetzt zu den Kenndaten ...
7Seg_2.gif
Hier sieht man die Durchlaßspannung der Segmente. Mit diesen Werten und
dem maximalen Strom kann man die Vorwiderstände der Segmente berechnen.
Wenn man die Durchlaßspannung nicht kennt, dann kann man sie auch auf
folgende Weise ermitteln ...
Projekt_2.gif
Man versorgt die LED in der Anzeige also über einen Vorwiderstand mit Strom
und mißt die Spannung über der LED. Ohne Vorwiderstand kann man die LED
wie bereits beschrieben zuverlässig verdampfen lassen :D :D

Aus diesen Werten werden wir als nächstes die Vorwiderstände berechnen
und erst mal nur eine Anzeige an den ATmega8 anschließen.

(wird fortgesetzt ... :D )
 
Zuletzt bearbeitet:
Hi Thomas,

von blindlinks kopieren spricht keiner! Mir get es bei meinen Beispiel auch nur um Hilfestellungen. Egal welches Lehrbuch Du aufschlägst, Du wirst in allen Lehrbüchern Basisbeschaltungen finden die es dem Einsteiger erleichtern grundlegende Dinge zu erlernen.
Dabei halte ich auch fest und warum so etwas nicht bei uns im Forum? Den Ansatz von Dino finde ich toll und ich kann nur sagen weiter so!

Wenn man mal grundlegende Basisbeschaltungen begriffen hat dann neigt man selber dazu die Dinge abzuändern und außerdem wächst man mit seinen Aufgaben und Herausforderungen.

Grüße,
Ma
 
Ist schon OK Markus,

Du weißt, ich bin in einigen Dingen ein bißchen anders ;)

Im Geschäft nennen sie mich auch den Minimalisten. Keine Zeile Code zuviel. Liegt wohl daran, das ich noch gelernt habe, mit 1k Speicher aus zukommen.

Thomas
 
yip.......
 
Hey Thomas,

ich weiß :p und schließlich kenne ich Dich ja lange genug um zu wissen wie es gemeint ist ;)

Grüße,
Ma
 
Basisschaltungen

Hallo , :D :D

Egal welches Lehrbuch Du aufschlägst, Du wirst in allen Lehrbüchern Basisbeschaltungen finden die es dem Einsteiger erleichtern grundlegende Dinge zu erlernen.
Dabei halte ich auch fest und warum so etwas nicht bei uns im Forum? Den Ansatz von Dino finde ich toll und ich kann nur sagen weiter so!
ich finde eine Basisschaltung als Grundlage/Puzzlestein eigentlich recht gut.
Man kann damit relativ schnell eine größere Schaltung zusammenpuzzeln.
Genau das mache ich eigentlich auch mit Beispielapplikationen aus Datenblättern
oder aus ApplicationNotes. Man sollte allerdings dann nach und nach erklären,
warum man bestimmte Dinge so zusammenschaltet und was man wann wie
verändern kann oder sollte. Ich versuche mal meine Gedankengänge bei der
Entwicklung eines Projektes hier zu erklären damit Anfänger den Verlauf so
einer Entwicklung mitbekommen und warum man was in welcher Reihenfolge
machen sollte.

Es kommt noch einiges hier rein. Laßt euch mal vom Ergebnis überraschen :D

Dann könnt ihr gerne meine Schaltungen und Gedankengänge verbessern
und eigene Ideen dazubringen. Wenn Anfänger mitbekommen wie ein
"Profi" tickt und wie man manche Sachen anfassen muß damit ein Schuh
dabei rauskommt ist das bestimmt nicht falsch :D

Na denn
Gruß
Dino
 
Es geht ein Licht auf - die erste Anzeige in Betrieb nehmen

... die Fortsetzung ...

Die Daten der 7-Segment-Anzeige kennen wir nun. Aber man sollte sich hüten
einfach die Anzeige mit den Vorwiderständen an den ATmega zu hängen und
dann loszulegen. Die Ports des ATmega vertragen auch nicht unendlich viele
Milliampere.

Electrical Characteristics

Absolute Maximum Ratings
Maximum Operating Voltage ............................................ 6.0V
DC Current per I/O Pin ............................................... 40.0 mA
DC Current VCC and GND Pins................................ 300.0 mA

DC Characteristics
TA = -40°C to 85°C, VCC = 2.7V to 5.5V (unless otherwise noted)

VOL || Output Low Voltage(3) (Ports B,C,D) || IOL = 20 mA, VCC = 5V || 0.7V

Notes 3.
Although each I/O port can sink more than the test conditions (20mA at Vcc = 5V, 10mA at Vcc = 3V) under steady state
conditions (non-transient), the following must be observed:
PDIP, TQFP, and QFN/MLF Package:
1] The sum of all IOL, for all ports, should not exceed 300 mA.
2] The sum of all IOL, for ports C0 - C5 should not exceed 100 mA.
3] The sum of all IOL, for ports B0 - B7, C6, D0 - D7 and XTAL2, should not exceed 200 mA.
If IOL exceeds the test condition, VOL may exceed the related specification. Pins are not guaranteed to sink current greater
than the listed test condition.
Die "Absolute Maximum Ratings" sind tabu! Da sollte man die Finger von
lassen. Sonst kann man den Baustein zuverlässig zerstören :D

Wenn man die 8 Segmente (A bis G und dp) der Anzeige summiert, dann
würde man bei 20mA Segment-Strom auf 160mA kommen. Das liegt noch im
Bereich der 200mA die bei Punkt 3 angegeben sind. Wir können also mit
20mA Segmentstrom arbeiten. Im Datenblatt der 7-Segment-Anzeige stand
ja auch das die Teile 20mA (grün/gelb) bzw 30mA (rot) vertragen. Wir sind
also mit 20mA bei der Anzeige und beim Prozessor im sicheren Bereich.

Jetzt berechnen wir mal die Vorwiderstände. die wir für die Segmente
benötigen.

- Betriebsspannung soll 5V sein
- Die LEDs der Anzeige haben 1,6V Durchlaßspannung (bei rot)
- Wenn der Output-Pin nach Low schaltet bleiben 0,7V übrig (bei 20mA)
- Wir wollen 20mA Segmentstrom nicht überschreiten

Das sind dann ...
5V - 1,6V - 0,7V = 2,7V die durch den Vorwiderstand vernichtet werden

2,7V / 20mA = 135 Ohm Vorwiderstand für die Segmente

da das ein zeimlich krummer Wert ist gehen wir auf den nächsten Wert der
E12-Widerstandsreihe hoch. Das sind dann 150 Ohm.

Jetzt rechnen wir mal zurück ...

2,7V / 150 Ohm = 18mA -> das ist OK :) wird genommen.

Damit sind die Vorwiderstände erledigt und wir schließen mal eine Anzeige
zum testen an.

( ... wird fortgesetzt ... )
 
Die erste Anzeige im Betrieb ...

und es geht weiter ...

Hier ist der Schaltplan mal um die erste Anzeige erweitert.
Projekt_3.gif

Die Anzeige ist am Port D angeschlossen und sollte auch leuchten wenn man
sie ansteuert. Und genau das wollen wir jetzt mal machen um die Hardware
zu testen ;)

Also muß ein kleines Progrämmchen her ...


CodeBox ASM

; ----------------------------
; Projekt: 7-Segment an Port D
; ----------------------------
;
.include "m8def.inc" ; Definitionsdatei laden
.cseg ; Beginn eines Code-Segmentes
.org 0 ; Startadresse=0
;
start:
ldi r16,low(ramend)
ldi r17,high(ramend)
out spl,r16 ; Stackpointer auf
out sph,r17 ; RAM-Ende setzen


ldi r16,0b11111111 ; PortD auf Ausgang ( alles auf 1 )
out ddrd,r16 ; setzen

ldi r16,0b00000000 ; Alle Pins auf Low zum Segmente anschalten
out portd,r16 ; setzen

mainloop: ; Die Hauptschleife
rjmp mainloop ; Schleife neu beginnen

Damit sollten alle Segmente leuchten (Also eine 8 mit Dezimalpunkt)

Damit ist der erste Hardwaretest erfolgreich durchgeführt :D
Wenn alles leuchtet und nichts abraucht dann wurde alles richtig verlötet.

(... wird fortgesetzt ... :) )
 
Zuletzt bearbeitet:
Muster bauen

weiter geht es mit ein wenig Design :D

Um Zeichen auf einem 7-Segment-Display anzeigen zu können muß man
wissen, welche Segmente bei einem bestimmten Zeichen leuchten sollen.

Code:
;   --a--
; |       |
; f       b
; |       |
;   --g--
; |       |
; e       c
; |       |
;   --d--  O dp
;
; Port D  -  7 6 5 4 3 2 1 0
; Segment - dp g f e d c b a
;
; Chr - Segmente - Port-Bit *=an(0), -=aus(1)
;       pgfedcba - 76543210
;  0  - --****** - 11000000
;  1  - -----**- - 11111001
;  2  - -*-**-** - 10100100
;  3  - -*--**** - 10110000
;  4  - -**--**- - 10011001
;  5  - -**-**-* - 10010010
;  6  - -*****-* - 10000010
;  7  - -----*** - 11111000
;  9  - -**-**** - 10010000
;  8  - -******* - 10000000
;  A  - -***-*** - 10001000
;  b  - -*****-- - 10000011
;  C  - --***--* - 11000110
;  d  - -*-****- - 10100001
;  E  - -****--* - 10000110
;  F  - -***---* - 10001110
;
sieht leider etwas Schlangenlinienmäßig aus ;)

Alle Segmente, die leuchten sollen, werden mit 0 ausgegeben. Die Segmente,
die dunkel bleiben sollen, bekommen eine 1.

Da die Anzeige mit gemeinsamer Anode ist, benötigt die Anzeige am Anschluß
für das Segment 0V (oder GND , wenn man es so nennen möchte). Dann
leuchtet das entsprechende Segment.

Damit haben wir für die Anzeigen sozusagen einen Zeichensatz erstellt.

( ... wird fortgesetzt ... )
 
Das sind dann ...
5V - 1,6V - 0,7V = 2,7V die durch den Vorwiderstand vernichtet werden

2,7V / 20mA = 135 Ohm Vorwiderstand für die Segmente

da das ein zeimlich krummer Wert ist gehen wir auf den nächsten Wert der
E12-Widerstandsreihe hoch. Das sind dann 150 Ohm.

Jetzt rechnen wir mal zurück ...

2,7V / 150 Ohm = 18mA -> das ist OK :) wird genommen.

Hallo Dino!

Du möchtest doch nachher die beiden Siebensegmente multiplexen, weil sie am gleichen Port hängen.....

Also wird jede Anzeige nicht ständig mit Spannung versorgt sondern immer nur "kurzzeitig".
Solltest du das bei der Berechnung des Vorwiderstandes nicht berücksichtigen?
Oder kommt das später noch?

Je nach Verhältnis zwischen Vein und Vpause wird die Anzeige dann ja später heller oder dunkler leuchten.


Gruß,
Cassio
 
Dauer- und Puls-Ströme bei LEDs

Hi Cassio,

Also wird jede Anzeige nicht ständig mit Spannung versorgt sondern immer nur "kurzzeitig".
Solltest du das bei der Berechnung des Vorwiderstandes nicht berücksichtigen?
Oder kommt das später noch?
Das ist mir schon klar :D
Da ist aber ein kleines Problem ... wenn jemand das Programm nicht sauber
zum laufen bekommt kann es zu "dauer an" von einer Anzeige kommen. Dann
kommt man aber von den zulässigen Pulsströmen der CPU-Pins und Segmenten
in die Dauerströme. Und dann kann man ziemlich sicher sein das man zwei
Glühwürmchen hat :D
Lassen wir also die Ströme für das Funktionsprinzip erst mal im Bereich des
Dauerstroms :) Sicher ist sicher :rolleyes:

Später werde ich die Schaltunng dann mit Treiber-ICs noch für höhere
Ströme und Spannungen umarbeiten.

Gruß
Dino
 
Arrays, Tabellen, Zeiger, Pointer

jetzt wollen wir mal anfangen, die Zeichen auf das Display zu zaubern :D

Man könnte jetzt für jedes Zeichen, das man anzeigen will eine Abfrage machen

wenn Zeichen = 3 dann PortD = 0b10110000
wenn Zeichen = 4 dann PortD = 0b10011001
....
...
..
.

Das wird aber ziemlich unübersichtlich, länglich und häßlich. :rolleyes:

Damit das hübsch und übersichtlich wird, benutzt man Tabellen oder
bei Basic/C Arrays. Über einen Zeiger/Pointer adressiert man dann eine
Zeile aus der Tabelle und liest sie aus.

Das sieht in Assembler so aus ...


CodeBox ASM

; ----------------------------
; Projekt: 7-Segment an Port D
; ----------------------------
;
.include "m8def.inc" ; Definitionsdatei laden
.cseg ; Beginn eines Code-Segmentes
.org 0 ; Startadresse=0
;
start:
ldi r16,low(ramend)
ldi r17,high(ramend)
out spl,r16 ; Stackpointer auf
out sph,r17 ; RAM-Ende setzen

ldi r16,0b11111111 ; PortD auf Ausgang ( alles auf 1 )
out ddrd,r16 ; setzen

ldi ZH,0x04 ; hohes Byte fuer Flash-Zugriff speichern
ldi ZL,0x05 ; niedriges Byte fuer Flash-Zugriff speichern
lpm r16,Z ; Daten aus Flash-Zelle holen Flash(ZH,ZL) => r16
out portd,r16 ; Segmente setzen

mainloop: ; Die Hauptschleife
rjmp mainloop ; Schleife neu beginnen



; ================================
; ===== 7-Segment-Anzeige ========
; ================================
; --a--
; | |
; f b
; | |
; --g--
; | |
; e c
; | |
; --d-- O dp
;
; Port D - 7 6 5 4 3 2 1 0
; Segment - dp g f e d c b a
;

.cseg ;===== 7-Segment-Zeichensatz =====
.org 0x0200 ;Startadresse der Tabelle ( H=0x04 , L=0x00 )
; => im Speicher liegt der Start wegen 16Bit dann bei 0x0200
; PC 0x0200 => Speicherstelle 0x0400 (wegen 16Bit-Zellen)
; Speicherzugriff mit ZH/ZL dann aber auf die Speicherstelle
;
; Die beiden Bytes des 16-Bit-Registers Z
; ===========ZH========== ===========ZL==========
; 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 Bit
; 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Bit
; ========================Z======================
; 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 H/L Bit => Programmzaehler
; Der Programmzaehler fuer das Flash arbeitet mit 16-Bit DATENzugriff
; Er adressiert also in WORD-Breite gegenueber dem Z-Register, das die
; Flash-Zellen in BYTE-Breite adressiert.
;
; 76543210 - PortD Bits
; pgfedcba - Segmente *=an(0), -=aus(1)
segmente: .db 0b11000000 ;0 0400 (0200) --******
.db 0b11111001 ;1 0401 (0200) -----**-
.db 0b10100100 ;2 0402 (0201) -*-**-**
.db 0b10110000 ;3 0403 (0201) -*--****
.db 0b10011001 ;4 0404 (0202) -**--**-
.db 0b10010010 ;5 0405 (0202) -**-**-*
.db 0b10000010 ;6 0406 (0203) -*****-*
.db 0b11111000 ;7 0407 (0203) -----***
.db 0b10010000 ;8 0408 (0204) -**-****
.db 0b10000000 ;9 0409 (0204) -*******
.db 0b10001000 ;A 040A (0205) -***-***
.db 0b10000011 ;b 040B (0205) -*****--
.db 0b11000110 ;C 040C (0206) --***--*
.db 0b10100001 ;d 040D (0206) -*-****-
.db 0b10000110 ;E 040E (0207) -****--*
.db 0b10001110 ;F 040F (0207) -***---*
; ZHZL PC

Die Tabelle ist ab der Flash-WORD-Adresse 0x0200 (Programmzelle).
Wenn man das jetzt Byte-Weise adressieren möchte, dann muß man
den doppelten Wert verwenden. Die Byte-Adresse im Flash lautet also
0x0400.

Die Tabelle ist jetzt im Flash. Aber wie kommen wir da ran?
Dafür gibt es die Registerpaare XH/XL , YH/YL und ZH/ZL.
Mit denen kann man über verschiedene Adressierungsmethoden
Speicherzellen im SRAM oder im Flash adressieren. Der Befehl LPM
(LoadProgramMemory) lädt uns ein Byte aus der Flash-Zelle, die
wir mit dem Z-Register (ZH/ZL) adressiert haben in das angegebene
Register. Das ist bei uns r16.

Wir holen also den Wert aus dem Flash in das Register r16, der durch
das 16bittige Z-Register adressiert wird. Z ist also unser Zeiger/Pointer
auf den Tabelleneintrag, den wir holen wollen.

Jetzt kommt aber der Trick! :D Wir wollen ja immer mal ein anderes
Zeichen anzeigen. Wie kann man das jetzt machen? Eigentlich ganz
einfach ;)

Wir haben die Tabelle klugerweise ab 0x0400 angelegt.
Diese 0 in der Adresse im ZL-Register paßt jetzt wunderbar mit der 0 in
verschiedenen Zahlensystemen zusammen :D
Die Ziffer 0 finden wir an der Stelle 0x0200
Die Ziffer 1 finden wir an der Stelle 0x0201
Die Ziffer 2 finden wir an der Stelle 0x0202
usw

Wir müssen also nur die Ziffer, die wir anzeigen wollen in die unteren 4 Bit
im ZL-Register laden :D und schon bekommen wir mit dem LPM-Befehl das
Bit-Muster für die Segmente der Anzeige zurückgeliefert. Ist das was?? ;)

Im Quellcode habe ich 0x05 in das ZL geladen und bekomme dann in r16
das Muster für die 5 zurückgeliefert.

Als nächstes werden wir mal einen kleinen Zähler auf der Anzeige bauen.

( ... wird fortgesetzt ... :D )
 
Ein 7-Segment Hex-Zähler

Jetzt lassen wir die 7-Segment-Anzeige mal zählen ...

Hier zuerst mal der Sourcecode ...


CodeBox ASM

; ----------------------------
; Projekt: 7-Segment an Port D
; ----------------------------
;
.include "m8def.inc" ; Definitionsdatei laden
.cseg ; Beginn eines Code-Segmentes
.org 0 ; Startadresse=0
;
start:
ldi r16,low(ramend)
ldi r17,high(ramend)
out spl,r16 ; Stackpointer auf
out sph,r17 ; RAM-Ende setzen

ldi ZH,0x04 ; hohes Byte fuer Flash-Zugriff speichern
ldi ZL,0x00 ; niedriges Byte fuer Flash-Zugriff speichern

ldi r16,0b11111111 ; PortD auf Ausgang ( alles auf 1 )
out ddrd,r16 ; setzen

clr r17 ; Zaehl-Register auf 0x00 setzen

mainloop: ; Die Hauptschleife

mov zl,r17 ; Zaehler in zl kopieren
cbr zl,0b11110000 ; Die oberen 4 Bits loeschen (wir haben nur 16 Zeichen definiert)

lpm r16,Z ; Daten aus Flash-Zelle holen Flash(ZH,ZL) => r16
out portd,r16 ; Segmente setzen

ldi r16,0x07 ; 7x 130ms ist in etwa 1 sec
rcall waitx130m ; r16 * 130ms warten

inc r17 ; Zaehler um 1 erhoehen

rjmp mainloop ; Schleife neu beginnen



; ==================================================================
; ===== r16 * 130ms warten =========================================
; ==================================================================
; rcall waitx130m ; (3) Warteschleife aufrufen - Zeit in r16
; 1MHz Systemtakt = 1us Zykluszeit fuer Befehle
; ================> ; 131843*x+18 Cyclen (incl Call+Ret) => ~x*130ms@1MHz
waitx130m: push r20 ; (2) r20 auf Stack retten (1tes)
push r21 ; (2) r21 auf Stack retten (2tes)
clr r20 ; (1) r16, r20 und r21 ergeben
clr r21 ; (1) zusammen einen 3-Byte-Zaehler
; 3_____________________________
; | 2____________131841_ |
; | | 1__513__ | |
; | | | | | |
; 123 Cycle = r16*((256*((256*2+1)+2)+1)+2)+1+17
loopx130m: dec r20 ; (1) ||| niedrigstes Byte -1
brne loopx130m ; (1f,2t)_/|| 0 erreicht? nein -> Schleife
dec r21 ; (1) || mittleres Byte -1
brne loopx130m ; (1f,2t)__/| 0 erreicht? nein -> Schleife
dec r16 ; (1) | höchstes Byte -1
brne loopx130m ; (1f,2t)___/ 0 erreicht? nein -> Schleife
;
pop r21 ; (2) r21 zurueckholen (2tes)
pop r20 ; (2) r20 zurueckholen (1tes)
ret ; (4) Schleifenende, Rueckkehr



; ================================
; ===== 7-Segment-Anzeige ========
; ================================
; --a--
; | |
; f b
; | |
; --g--
; | |
; e c
; | |
; --d-- O dp
;
; Port D - 7 6 5 4 3 2 1 0
; Segment - dp g f e d c b a
;

.cseg ;===== 7-Segment-Zeichensatz =====
.org 0x0200 ;Startadresse der Tabelle ( H=0x04 , L=0x00 )
; => im Speicher liegt der Start wegen 16Bit dann bei 0x0200
; PC 0x0200 => Speicherstelle 0x0400 (wegen 16Bit-Zellen)
; Speicherzugriff mit ZH/ZL dann aber auf die Speicherstelle
;
; Die beiden Bytes des 16-Bit-Registers Z
; ===========ZH========== ===========ZL==========
; 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 Bit
; 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Bit
; ========================Z======================
; 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 H/L Bit => Programmzaehler
; Der Programmzaehler fuer das Flash arbeitet mit 16-Bit DATENzugriff
; Er adressiert also in WORD-Breite gegenueber dem Z-Register, das die
; Flash-Zellen in BYTE-Breite adressiert.
;
; 76543210 - PortD Bits
; pgfedcba - Segmente *=an(0), -=aus(1)
segmente: .db 0b11111001 ;0 0400 (0200) --******
.db 0b10100100 ;1 0400 (0200) -----**-
.db 0b10100100 ;2 0400 (0200) -*-**-**
.db 0b10110000 ;3 0400 (0200) -*--****
.db 0b10011001 ;4 0400 (0200) -**--**-
.db 0b10010010 ;5 0400 (0200) -**-**-*
.db 0b10000010 ;6 0400 (0200) -*****-*
.db 0b11111000 ;7 0400 (0200) -----***
.db 0b10010000 ;8 0400 (0200) -**-****
.db 0b10000000 ;9 0400 (0200) -*******
.db 0b10001000 ;A 0400 (0200) -***-***
.db 0b10000011 ;b 0400 (0200) -*****--
.db 0b11000110 ;C 0400 (0200) --***--*
.db 0b10100001 ;d 0400 (0200) -*-****-
.db 0b10000110 ;E 0400 (0200) -****--*
.db 0b10001110 ;F 0400 (0200) -***---*
; ZHZL PC

Die Warte-Routine von Zeile 40-64 nehmen wir jetzt einfach mal als gegeben
hin. Die Routine wird in der Hauptschleife als 1sec Verzögerung benutzt.

Das Register r17 hab ich als Zähler verwendet. Dieses Register wird bei jedem
Durchlauf um eins erhöht. Da wir bei Hex für eine Stelle nur 4 Bit benötigen,
das Register aber 8 Bit hat, blenden wir einfach die obersten 4 Bit des
Zählers nach dem kopieren von r17 in zl aus. Die restlichen 4 Bit werden
dann mit zh zusammen zum Pointer auf den Tabelleneintrag des 7-Segment
Musters.

Das ganze Konstrukt wird also auf der 7-Segment-Anzeige im Abstand von
einer Sekunde hochzählen.

0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , A , b , C , d , E , F , 0 , 1 , 2 , ...

Dieses war der erste und wichtigste Schritt um was lesbares auf die
Anzeige zu bekommen.

Jetzt machen wir daraus einen zweistelligen Hex-Zähler ...

( ... wird fortgesetzt ... :) )
 

Ü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)