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)
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?
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.
Hab ich Forum gelesen, und mußte des hier gleich mal ausprobier. ;-) Gruß Marco
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
"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.
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
(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?
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?"
"...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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.