Forum: Mikrocontroller und Digitale Elektronik LED Multiplex ansteuern


von Sven Scholz (Gast)


Lesenswert?

Hallo,

ich habe folgende Schaltung aufgebaut und möchte diese nun in Betrieb
nehmen...

http://www.klaus-leidinger.de/mp/Mikrocontroller/7Segment/7Segment.html#oben

Ich benutze zur Ansteuerung den ATmega8 und verwende AVRStudio4 von
Atmel.
Da ich absoluter MC-Neuling bing, bräuchte ich etwas Unterstützung
hinsichtlich der Assembler-Programmierung.

Zunächst einmal möchte ich einen Zähler programmieren, der von 0000 bis
9999 hochzählt.
Hat vielleicht jemand einen passenden Quellcode hierfür oder eine
Hex-Datei, damit ich die Funktion der Schaltung überprüfen kann?

Für die Ansteuerung der einzelnen Segmente verwende ich den Port B.
Für das An- und Abschalten der 7-Segment-Anzeige habe ich den Port D
vorgesehen...


Vielen Dank schon mal.
(hoffentlich kann mir geholfen werden)

von Sven Scholz (Gast)


Lesenswert?

Hat denn noch niemand von euch so etwas programmiert? Ich dachte, dass
ich einen passenden Quellcode dafür, wie "Sand am Meer" finden würde
im Internet. Scheint doch wohl nicht so einfach zu sein wie?

von Marco K. (Gast)


Angehängte Dateien:

Lesenswert?

Servus Sven,
es ist nicht so das keiner 7-Seg. benutzt, aber die meisten benutzen
zum ansteuern noch ein Extra IC (I2C oder 4-Bit) um Pin's am
µ-Controller, Leitungen, Programmieraufwand und natürlich Rechenzeit zu
sparen. Da ich aber weiß wie schwer der Einstieg ins µ-Controller
programmieren ist geb ich dir hier mal ein paar Ansätze wie du dein
Programm schreiben könntest:
1
;**** Includes ****
2
3
.include "m8def.inc"
4
5
;***** Pin definitions
6
7
.equ  SSeg  = PORTB      ;Ausgabe Port
8
.equ  SSegdr  = DDRB      ;Ausgabe Richtungsregister
9
.equ  punkt  = 7        ;'Punkt-Bit'
10
11
;***** Global register variables
12
13
.def  temp  = R16      ;Register zum Zwischenspeichern
14
.def  zahl  = R17      ;Zahl die ausgegeben werden soll
15
16
17
18
19
ausgabe:
20
  ldi   ZH,high(ziffer<<1)  ;Z-Pointer mit Adresse 
21
  ldi   ZL,low(ziffer<<1)  ;  der Tabelle laden
22
  adc    ZL, zahl      ;Auszugebende Zahl mit Z-Pointer (LOW) addieren
23
  brcc  nc          ;Testen ob Carry-Flag gesetzt ansonsten springen
24
  inc    ZH          ;Z-Pointer (HIGH) Inkrementieren
25
nc:  lpm    temp, Z        ;Daten aus Tabelle laden
26
  out    SSeg, temp      ;Zahl auf 7-Segment ausgeben
27
28
  cbi    SSeg, punkt      ;Schaltet Punkt ein
29
  sbi    SSeg, punkt      ;Schaltet Punkt aus
30
  ret
31
32
ziffer:
33
  .db $81, $B7, $C2, $92, $B4, $B4, $98, $88, $B3, $80, $90 ;Tabelle mit
34
Ziffern von 0-9
Das wäre ein Unterprogramm um auf einer 7-Segment-Anzeige eine Zahl zu
bringen. Mit Tabellen zu arbeiten sieht am Anfang oft kompliziert aus,
ist aber extrem praktisch. Um die Befehle besser zu Verstehen zu können
hab ich dir noch das ATMEL Instruction-Set mit angehängt, da sind alle
Befehle der AVR's beschrieben.

von Tobias Schneider (Gast)


Lesenswert?

Seit wann gibts denn hier source highlighting?

Gruss Tobias

von Marco K. (Gast)


Lesenswert?

Hab ich Forum gelesen, und mußte des hier gleich mal ausprobier. ;-)

Gruß Marco

von Marco K. (Gast)


Lesenswert?

Servus Sven,

Ich hab auch noch ne Routine für dich gefunden die dir eine 16-Bit Zahl
in BCD umwandelt. Die BCD Zahlen kannst du dann auf deine 7-Segment
Anzeige ausgeben:

http://www.avr-asm-tutorial.net/avr_de/rechnen/konvert.html#bin2bcd

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

"Hat denn noch niemand von euch so etwas programmiert?"

Doch, schon.

Aber als Anfänger sollte man lieber schrittweise vorgehen (LED an, aus,
blinken, Timerinterrupt usw.).


Fremde Quelltexte zu verstehen, fällt selbst alten Programmierhasen
schwer. Daher bezweifle ich stark, daß Dir ein fertiges Progamm was
nützt. Trotzdem hab ich mal eine einfache ADC-Anwendung rangehängt.

Fürs einfachere Layout haben Digit 0 und 2 bzw. Digit 1 und 3
unterschiedliche Pinbelegung, deshalb 2 7-Segmentkodetabellen.

Noch ein Trick dabei ist, daß der Timerinterrupt den ADC startet und
erst nach der Wandlung das nächste Digit eingeschaltet wird. Ansonsten
bewirkt nämlich der Stromverbrauch der LEDs kleine
Spannungsschwankungen und der Meßwert zappelt ständig.

Anders gesagt, ohne ADC macht man die Digitausgabe einfach direkt im
Timerinterrupt.


Aber wie gesagt, ohne Grundlagenkenntnisse, wird der Code wohl mehr
Fragen aufwerfen anstatt beantworten. Obwohls nur ein sehr kleines
Programm ist (sind ja nur 89 Words).


Peter


P.S.:
Man kriegt hier auch viel leichter Hilfe, wenn man seine eigenen
Lösungsschritte darstellt und konkrete Fragen hat, anstatt nach einer
Fixundfertig-Lösung zu schreien.

von Sven Scholz (Gast)


Angehängte Dateien:

Lesenswert?

Also ich habe jetzt meine ersten Gehversuche hinter mir und mein erstes
Ziel mehr oder weniger umgesetzt. Ist eine ganz schön Arbeit, das Ganze
richtig zeitlich hinzubekommen.

Habe den Quellcode als Anhang beigefügt. Vielleicht könnt ihr da ja mal
reinschauen und einige Bemerkungen dazu machen.

Mein nächstes Ziel ist es jetzt, unterschiedliche Ziffern für das
jeweilige Display auszugeben. Bis jetzt kann ich halt nur eine Ziffer
an alle 4 Displays schicken, die dann gleichzeitig angezeigt wird.
(z.B. 6666, 8888, 4444)

Bin aber jetzt ganz zuversichtig, dass ich das noch irgendwann mal
hinkriegen werde. Wenn man bedenkt, dass ich mich jetzt erst seit 8
Tagen mit dem Thema beschäftige.

Danach wäre eine Realisierung eines richtigen Counters angesagt, der
von 0000 bis 9999 in einer Schritten hochzählt.

Also, wenn ihr produktive Ideen oder Quellcodeschnipsel für mich habt,
dann bitte her damit...

Danke schon mal für die brauchbaren Tipps!

MfG,

Sven

von Sven Scholz (Gast)


Lesenswert?

(siehe Anhang vom Beitrag zuvor)

init:ldi r21,0x00 ;      hier steht sozusagen mein offset



main:

  ldi   ZH,high(zahlen<<1)  ;Z-Pointer mit Adresse
  ldi   ZL,low(zahlen<<1)  ;  der Tabelle laden
  adc    ZL, r21      ;Auszugebende Zahl mit Z-Pointer (LOW) addieren
  lpm zahl1, Z ; Load constant from Program
  lpm zahl2, Z;
  lpm zahl3, Z;
  lpm zahl4, Z;
  inc    ZH          ;Z-Pointer (HIGH) Inkrementieren

  cpi r21,10
  breq init          ; Sprung wenn Vergleich stimmt
    inc r21           ;für nächste Ziffer


  RCALL ausgabe
  rjmp main







Wie schaffe ich es nun, zahl2 immer dann um eins zu erhöhen, wenn zahl1
die 0 erreicht?

Ich brauche irgendwie mehr Zeiger, die meine "Zahlen-Tabelle" lang
wandern. Gibt es dafür irgendwie eine elegante Methode?

von Sven Scholz (Gast)


Lesenswert?

das ist ja so kompliziert...

also bin jetzt soweit, dass ich von 0000 bis 0099 hochzählen kann, doch
wird es jetzt zunehmend komplizierter, so dass ich mich langsam selbst
nicht mehr in meinem eigenen Quellcode zurechtfinde.

Brauche Unterstützung bei der Sache.
Ist da draußen nicht irgendwo ein "Assembler-Gott?"

von Peter Dannegger (Gast)


Lesenswert?

"...doch wird es jetzt zunehmend komplizierter..."


Es ist immer am einfachsten, die CPU in ihren Hausformat rechnen zu
lassen, also im Binärsystem.
Mit 2 Bytes kann man dann bis 65535 zählen.
Und erst zur Anzeige wandelt man in das Dezimalsystem um.
Für den Anfang ist es aber sinnvoll, erstmal nur ein Byte zu zählen und
anzuzeigen, also von 0 ... 255. Und wenn man das verstanden hat, weiter
zu gehen.

Wichtig ist es, alles in kleine Teilaufgaben zu zerlegen und nicht
gleich alles zusammen machen zu wollen.


Schau Dir mal in meinem Beispiel die Routine "decout" an.

Die macht nichts anderes, als eine 16Bit-Zahl in ihre 5 Digits zu
zerlegen.
Das 1. Digit wird weggeschmissen, da ich nur 4-stellig anzeige.
Und die anderen 4 Digits werden per LPM in den 7-Segmentcode gewandelt
und in den Anzeigeregistern abgelegt, von wo sie dann per
Timerinterrupt zyklisch ausgegeben werden.

Die Binär nach Dezimal Wandlung selber geschieht nach dem
Subtraktionsprinzip, d.h. man zählt einfach mit, wie oft 10000, 1000,
100 und 10 ohne Unterlauf abgezogen werden können.
Die Routine ist dann noch optimiert, indem absichtlich immer über den
Unterlauf bzw. Überlauf hinaus gezählt wird.


Peter

von Marco K. (Gast)


Lesenswert?

Servus Sven,

Respekt, für den den Anfang nicht schlecht.
Das mit dem riesigen unübersichtlichen Code ist typisch für Assembler,
da muss man wirklich sauber Programmieren und alles Aufteilen damit
sich später noch zurecht findet. Ein kleiner Tipp: Soviel wie möglich
mit Kommentaren versehen, sonst weiß man 2 Wochen später selbst nicht
mehr was welcher Befehl bewirken soll. Und mit der Zeit findest du auch
deinen eigenen Stil.
Zu deinem Programm:

An deiner Stelle würde ich wie Peter schon sagte zwei Register zum
Zählen nehmen und die vor jeder ausgabe in 4 BCD Zahlen umwandeln
lassen.
Zum Rückwärtszählen hät ich dir ein kleines Beispiel, dass mit dem
Y-Register (16 Bit) arbeitet.
1
init:
2
ldi   YL, $10      ;9999 in Y-Register laden
3
ldi   YH, $27
4
5
start:
6
rcall   ausgabe    ;Springe zum Ausgabe UP
7
8
sbiw  YH:YL,1      ;Y-Register dekrementieren
9
brne  init         ;Teste, ob Y = 0
10
rcall delay_loop   ;kurze Pause
11
jmp   start        ;Nächste Zahl

Gruß Marco

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.