Forum: Mikrocontroller und Digitale Elektronik Assembler, Counter, 7Segment


von Holger S. (capa)


Lesenswert?

Hi kann mir vielleicht einer sagen ob mein Sourcecode so funktionieren 
würde ?

MC : ATmega32
Funktionsweise:
Eine einfache 2 Team Punktestandsanzeige, wenn ich auf +/- klicke
(je nachdem welche seite gewählt) soll der entweder hoch oder runter
zählen, und beim resettaster soll er auf 0 zurückstellen und wieder von
vorne anfangen wenn man die tasten (+/-) anklickt.

Das ganze soll für nen arbeitskollegen ne anzeige werden für seine 
Dartgemeinschaft :) leider hab ich die hardware noch nicht fertig da
die materialien noch aufm weg sind und kann das ganze nicht testen.
vielleicht hat ja jemand schon sowas aufgebaut und kann den code mal 
testen
bzw vielleicht findet sogar jemand nen fehler, bin was asm angeht
eigentlich neuling, hab bisher nur sprachen wie delphi, php etc 
programmiert.

Mfg und danke schonmal im vorraus :)



--------
-------- B >  5 Taster (0-4)
--------
Atmega - C > 2 x BCD-7Segment-Dekoder
--------
-------- D > 2 x BCD-7Segment-Dekoder
--------

1
/* ------------------------------------
2
 * - Dart-Team-Counter                -
3
 * ------------------------------------
4
 * -  > Version: 1.0                  -
5
 * -  > Author : Holger Schreiber     -
6
 * -  > Created: 07.02.2012           -
7
 * ------------------------------------
8
 */ 
9
10
11
.include "m32def.inc"
12
13
; *************
14
; * Variablen *
15
; *************
16
17
.def team1_feed = r1
18
.def team2_feed = r2
19
20
.def team1 = r16
21
.def team2 = r17
22
.def team1_1 = r18
23
.def team2_1 = r19
24
.def temp  = r20
25
26
.equ team1_led_ddr    = ddrc
27
.equ team1_led_port   = portc
28
29
.equ team2_led_ddr    = ddrd
30
.equ team2_led_port   = portd
31
32
.equ taster           = ddrb
33
; *************
34
; * Variablen *
35
; *************
36
37
38
.cseg
39
.org 0
40
  rjmp main
41
42
43
main:
44
    ldi     ZL, LOW(tabelle*2)  ; die Startadresse der Tabelle in den
45
    ldi     ZH, HIGH(tabelle*2) ; Z-Pointer laden
46
 
47
    ; PortC/D auf Ausgang setzen
48
    ldi    temp, 0xFF
49
    out    team1_led_ddr, temp
50
51
    ldi    temp, 0xFF
52
    out    team2_led_ddr, temp
53
54
    ; PortB auf Eingang setzen
55
    ldi    temp, 0x00
56
    out    taster, temp
57
    rcall   setnull
58
  rjmp loop
59
60
61
loop:                       ; Taster abfragen +/-/reset
62
    SBIC PINB,0             ; Taster 1 = Team1 +
63
    rcall setteam1
64
65
    SBIC PINB,1             ; Taster 2 = Team1 -
66
    rcall resetteam1
67
68
    SBIC PINB,2             ; Taster 3 = Team2 +
69
    rcall setteam2
70
71
    SBIC PINB,3             ; Taster 4 = Team2 -
72
    rcall resetteam2
73
 
74
    SBIC PINB,4             ; Taster 5 = Reset
75
    rcall setnull
76
  rjmp loop
77
78
79
setnull:
80
    out team1_led_port, 0b00000000
81
    out team2_led_port, 0b00000000
82
83
    ldi     team1, 0          ; und den Zähler initialisieren
84
    mov     team1_feed, team1
85
86
    ldi     team2, 0          ; und den Zähler initialisieren
87
    mov     team2_feed, team2
88
  ret
89
90
91
92
setteam1:
93
    inc     team1                        ; den Zähler erhöhen, wobei der Zähler
94
    cpi     team1, 20                    ; immer nur von 0 bis 9 zählen soll
95
96
    mov     team1_1, team1               ; die wortweise Adressierung der Tabelle
97
    add     team1_1, team1               ; berücksichtigen
98
99
    add     ZL, team1_1                  ; und ausgehend vom Tabellenanfang
100
    adc     ZH, team1_feed               ; die Adresse des Code Bytes berechnen
101
102
    lpm                                  ; dieses Code Byte in das Register r0 laden
103
    out     team1_led_port, r0           ; und an die Anzeige ausgeben
104
  ret
105
106
resetteam1:
107
    dec     team1                        ; den Zähler erhöhen, wobei der Zähler
108
    cpi     team1, 20                    ; immer nur von 0 bis 9 zählen soll
109
110
    mov     team1_1, team1               ; die wortweise Adressierung der Tabelle
111
    add     team1_1, team1               ; berücksichtigen
112
113
    add     ZL, team1_1                  ; und ausgehend vom Tabellenanfang
114
    adc     ZH, team1_feed               ; die Adresse des Code Bytes berechnen
115
116
    lpm                                  ; dieses Code Byte in das Register r0 laden
117
    out     team1_led_port, r0           ; und an die Anzeige ausgeben
118
  ret
119
120
setteam2:
121
    inc     team2                        ; den Zähler erhöhen, wobei der Zähler
122
    cpi     team2, 20                    ; immer nur von 0 bis 9 zählen soll
123
124
    mov     team2_1, team1               ; die wortweise Adressierung der Tabelle
125
    add     team2_1, team1               ; berücksichtigen
126
127
    add     ZL, team2_1                  ; und ausgehend vom Tabellenanfang
128
    adc     ZH, team2_feed               ; die Adresse des Code Bytes berechnen
129
130
    lpm                                  ; dieses Code Byte in das Register r0 laden
131
    out     team2_led_port, r0           ; und an die Anzeige ausgeben
132
  ret
133
134
resetteam2:
135
    dec     team2                        ; den Zähler erhöhen, wobei der Zähler
136
    cpi     team2, 20                    ; immer nur von 0 bis 9 zählen soll
137
138
    mov     team2_1, team1               ; die wortweise Adressierung der Tabelle
139
    add     team2_1, team1               ; berücksichtigen
140
141
    add     ZL, team2_1                  ; und ausgehend vom Tabellenanfang
142
    adc     ZH, team2_feed               ; die Adresse des Code Bytes berechnen
143
144
    lpm                                  ; dieses Code Byte in das Register r0 laden
145
    out     team2_led_port, r0           ; und an die Anzeige ausgeben
146
  ret
147
148
149
tabelle:
150
  .db     0b00000000  ; 0
151
  .db     0b00000001  ; 1
152
  .db     0b00000010  ; 2
153
  .db     0b00000011  ; 3
154
  .db     0b00000100  ; 4
155
  .db     0b00000101  ; 5
156
  .db     0b00000110  ; 6
157
  .db     0b00000111  ; 7
158
  .db     0b00001000  ; 8
159
  .db     0b00001001  ; 9
160
  .db     0b00010000  ; 10
161
  .db     0b00010001  ; 11
162
  .db     0b00010010  ; 12
163
  .db     0b00010011  ; 13
164
  .db     0b00010100  ; 14
165
  .db     0b00010101  ; 15
166
  .db     0b00010110  ; 16
167
  .db     0b00010111  ; 17
168
  .db     0b00011000  ; 18
169
  .db     0b00011001  ; 19
170
  .db     0b00100000  ; 20

von Karl H. (kbuchegg)


Lesenswert?

Die Sache mit den Tastern funktioniert schon mal so nicht. So kurz 
kannst du gar nicht drücken, dass der µC mit deinem Code nicht bis 
mindestens 10000 zählt.

von Karl H. (kbuchegg)


Lesenswert?

Der Mega32 initialisiert meines Wissens den Stackpointer nicht selber. 
Das musst du machen.

von Karl H. (kbuchegg)


Lesenswert?

?
1
    cpi     team1, 20                    ; immer nur von 0 bis 9 zählen soll
2
3
    mov     team1_1, team1               ; die wortweise Adressierung der Tabelle

wozu soll das Vergleichen von team1 mit 20 gut sein (wieso eigentlich 
20? im Kommentar steht 9), wenn du dann das Verlgeichsergebnis überhaupt 
nicht weiter beachtest?

von Einer (Gast)


Lesenswert?

Die Grenzwerttests sind so noch nicht funktionsfähig.

von Karl H. (kbuchegg)


Lesenswert?

Tu dir selbst einen Gefallen und fang im AVR-Tutorial ganz vorne an. Das 
hat keinen Sinn, wenn du da wesentliche Teile überspringst. Selbst wenn 
du bereits delphi, php, etc. Erfahtrung hast. Assembler ist nun mal 
kleinster gemeinsamer Nenner. Da musst du dich um alles selbst kümmern. 
Und wenn du nicht weißt was, kannst du es auch nicht tun. Daher: 
Tutorial von vorne durchgehen. Da du schon programmierern kannst, wirst 
du die einzelnen Kapitel schnell durchhaben. Bei jedem Kapitel ein, zwei 
eigene Übungen und dann klappt das auch.


Oder, alternativ:
Da du ja schon andere höhere Programmiersprachen kennst, warum dann 
nicht einfach C? Da musst du dich dann um den ganzen Kleinkram nicht 
selber kümmern.

Dein bisheriges Programm würde in C so aussehen (ohne dein Tastenproblem 
damit gelöst zu haben)
1
#include <avr/io.h>
2
3
uint8_t team1Points;
4
uint8_t team2Points;
5
6
// die Tastenpins am Port B
7
#define T1_UP     0
8
#define T1_DOWN   1
9
#define T2_UP     2
10
#define T2_DOWN   3
11
#define T_RESET   4
12
13
uint8_t SegCode[] = {
14
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
15
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19
16
};
17
18
int main()
19
{
20
  // Pullups für die Taster
21
  PORTB = ( 1 << T1_UP ) | ( 1 << T1_DOWN ) |
22
          ( 1 << T2_UP ) | ( 1 << T2_DONW ) |
23
          ( 1 << T_RESET );
24
25
  // LED Ausgänge
26
  DDRC = 0xFF;
27
  DDRD = 0xFF;
28
29
  while( 1 ) {
30
31
    if( PINB & ( 1 << T1_UP ) ) {
32
      if( team1Points < 19 )
33
        team1Points++;
34
    }
35
36
    if( PINB & ( 1 << T1_DOWN ) ) {
37
      if( team1Points > 0 )
38
        team1Points--;
39
    }
40
41
    if( PINB & ( 1 << T2_UP ) ) {
42
      if( team2Points < 19 )
43
        team2Points++;
44
    }
45
46
    if( PINB & ( 1 << T2_DOWN ) ) {
47
      if( team2Points > 0 )
48
        team2Points--;
49
    }
50
51
    if( PINB & ( 1 << T_RESET ) ) {
52
      team1Points = 0;
53
      team2Points = 0;
54
    }
55
56
    PORTC = SegCode[ team1Points ];
57
    PORTD = SegCode[ team2Points ];
58
  }
59
}
kommt dir als PHP Programmierer von der Syntax her bekannt vor?

von Holger Schreiber (Gast)


Lesenswert?

Ok das mit dem taster würde ich dann mit ner warteschleife von 1-2 sek 
lösen
das der quasi nach dem ausführen des codes einfach die schleife 
abarbeitet
und erst danach wieder einen durchlauf zulässt.

stackpointer mhhh darüber sollte ich mich nochmal informieren das 
stimmt.

1
    cpi     team1, 20                    ; immer nur von 0 bis 9 zählen soll

Hab nur vergessen den Kommentar anzupassen, das mit dem zähler hab ich 
aus
ner anderen funktion die ich gefunden hab rausgenommen.
dort stand das auch mit dem cpi drin sollte wohl dafür sorgen das der 
dann ab 20 wieder von vorne anfängt.
Gibt es da ein paar tutorials die direkt das thema atmega asm behandeln 
?
Hab nur eine seite wo es eine befehlsübersicht gibt zum thema avr und 
asm.
Ein Tutorial hatte ich auch schon aber da wurde nur ein kleiner teil des
ganzen behandelt und somit sind da noch mehrere fragen offen.

Kurz und knapp, kann mir jemand ein paar gute Tutorials zum thema
AVR-Assembler Programmierung sagen ?

von Karl H. (kbuchegg)


Lesenswert?

Holger Schreiber schrieb:
> Ok das mit dem taster würde ich dann mit ner warteschleife von 1-2 sek
> lösen
> das der quasi nach dem ausführen des codes einfach die schleife
> abarbeitet
> und erst danach wieder einen durchlauf zulässt.

Gaaaaanz schlechte Idee. Da werden dich deine Teams lieben, wenn sie mal 
schnell den Punktestand um 5 Punkte korrigieren müssen. Bei 
Tasterbetätigungen ist das beste gerade gut genug. Das ist das, was 
deine Benutzer bei jeder Gerätebenutzung von deiner Arbeit am meisten 
mitkriegen. Und schlechte Tastenauswertung kann einen zur Weißglut 
treiben, wenn man auf dem Taster rumhämmert und alles mögliche passiert 
nur nicht die angezeigte Zahl erhöht oder verringert sich um genau und 
exakt 1. Wer an dieser Stelle den Aufwand spart, hat es nicht besser 
verdient als dass ihn seine Benutzer zum Teufel wünschen. Zumal der 
Aufwand gar nicht mal so groß ist.

Im Tutorial ist bei den Tasterbetätigungen eine Funktionalität drinnen, 
die das viel besser kann.


> Gibt es da ein paar tutorials die direkt das thema atmega asm behandeln
> ?

AVR-Tutorial

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Holger Schreiber schrieb:
> Hi kann mir vielleicht einer sagen ob mein Sourcecode so funktionieren
> würde ?
Der Simulator?
Allerdings findet der "nur" Programmfehler. Systemfehler (z.B. nicht 
entprellte Taster) lassen sich nur schwer simulieren.


OT:
Holger Schreiber schrieb:
> Gibt es da ein paar tutorials die direkt das thema atmega asm behandeln
> ?
Lass das Leerzeichen vor dem Fragezeichen weg. Beim Punkt gehts ja auch. 
So ein unnötiges Space macht den Text nicht besser lesbar. Dafür gibt 
es sogar einen eigenen Begriff: Plenken

von Bernd S. (Firma: Anscheinend Corner-Cases ;-)) (bernd_stein)


Lesenswert?

Holger Schreiber schrieb:
>...
> Kurz und knapp, kann mir jemand ein paar gute Tutorials zum thema
> AVR-Assembler Programmierung sagen ?
>
http://www.weigu.lu/a/index.html

Bernd_Stein

von spess53 (Gast)


Lesenswert?

Hi

>http://www.weigu.lu/a/index.html

>Bernd_Stein

Reichlich angestaubt und recht unvollständig.

MfG Spess

von Hannes L. (hannes)


Angehängte Dateien:

Lesenswert?

spess53 schrieb:
> Reichlich angestaubt und recht unvollständig.

Stimmt, damit kann man keine ernstzunehmenden Programme schreiben.

Bei der Beschreibung des Doppelregisters W bekomme ich das Gefühl, dass 
die von mir abgeschrieben haben. Ich benenne r24/r25 schon recht lange 
so.

...

von oldmax (Gast)


Lesenswert?

Hi
Nunja, sicher macht eine Hochsprache das Zählen von Punkten einfacher, 
aber Assembler ist sicherlich auch kein Problem. Ich finde, so ein paar 
Dinge sind im Prinzip ja auch schon richtig angesetzt. Programmeinstieg 
(MAIN) mit Initialisierungen. Hier gehört noch die Initialisierung des 
Stackpointers an die erste Stelle. Klar, weil jeder Call den Stack 
braucht. In der programmschleife (LOOP) würd ich etwas anders vorgehen.
Ich hab's noch mit "EVA" gelernt. "Einlesen", "Verarbeiten", "Ausgeben".
Daher mein Tip
:
Lesen der Eingänge und ablegen in einer Variablen. Nicht gleich 
schreien, aber Variablen brauchen auch nicht so viel mehr Zeit und 
machen ein Programm übersichtlicher. Außerdem ist eine bessere 
Portierbarkeit durch Kapselung möglich. Aber das ist mein Stil.
Und nun, da du ja auch in Delphi programmierst, erinner dich mal, was da 
entscheident ist: "Ereignisse".
Auch einen Atmega kannst du mit "Ereignissen" steuern. Ein Tastendruck 
ist ein Ereignis. KeyPressed, sagt dir sicherlich etwas.
Du bekommst dieses Ereignis, wenn du dir den letzten gelesenen Zustand 
merkst und mit einem aktuellen Zustand vergleichst. Ein "EOR" sagt dir, 
welche Eingänge sich geändert haben und eine Und-Verknüpfung mit dem 
alten Wert sagt: Die Änderung ging von "0" nach "1". Machst du eine 
"und"-Verknüpfung mit dem Ergebnis der "EOR" und den neuen eingelesenen 
Werten, dann erhälst du den Wechsel von "0" nach "1".
Diese Bits nutzt du, um bei der Bearbeitung das Ereignis zu bearbeiten. 
So ist ein gesetztes Bit von "0" nach "1" z.B. in einer Variablen 
"In_to_High" abzulegen. Im Programm prüfst du den Zustand und 
bearbeitest das Ereignis, wenn gesetzt. Dann löscht du in deiner 
Bearbeitung das Bit. Ergebnis: du mußt für eine weitere Bearbeitung den 
Taster loslassen und erneut drücken. Damit kannst du z.B. deinen Zähler 
hoch oder runter zählen.
Nun zu den nicht betrachteten Fallen, das prellen von Tastern. Auch die 
Bitlage sollte ich ansprechen. Fangen wir mit dem Prellen an. Ein 
Controller ist so schnell, das ein Tastendruck durchaus x-mal gesehen 
wird. Das würde deinen Zähler undefiniert "springen" lassen. Ein 
Kondensator parallel zum Taster hilft Hardwaremäßig. In der Software 
wartet man einfach eine kleine Zeit, bis sich der Eingang nicht mehr 
ändert. Auch hier ist "EOR" sehr hilfreich. Zur Signallage auch noch ein 
Wort. Da die Prozesoren eingebaute Pull-Up-Widerstände haben, sind die 
Taster in der Regel gegen GND gesachaltet. Das bedeutet, ein gedrückter 
Taster entspricht einer "0". Somit ist auch die Bearbeitung darauf 
auszulegen und das ist nicht immer einfach, in dieser "negierten Logik" 
zu denken- daher drehe ich Grundsätzlich die Eingänge so, das ein 
geschlossener Schalter auch einer "1" entspricht.
Ich hab schon oft eine solche Routine hier abgelegt, das ich nicht 
nochmal im Einzelnen drauf eingeh, aber such mal nach Entprellen. Da 
findest du viele Tips.
Zum Schluß noch ein kleiner Hinweis: wenn du in Delphi programmierst, 
dann ist es doch sinnvoller, einem Atmega eine RS232 zu spendieren und 
erfasste Daten auf einen PC (Netbook oder Notebook) zu schicken. Dort 
wird nicht nur archiviert, sondern auch schön visualisiert. Aber... wenn 
ich mir das dann so überlege, wofür braucht's dann noch einen Atmega ?
Gruß oldmax

von oldmax (Gast)


Lesenswert?

Hi
Ich sollte mich anmelden, damit ich meine Fehler auch korrigieren 
kann.....
u.A.
>Da die Prozesoren eingebaute Pull-Up-Widerstände haben, sind die
>Taster in der Regel gegen GND gesachaltet.
"gesachaltet" hab ich bisher noch keine Eingänge......

Das sollte ich vielleicht auch noch ergänzen:
>In der Software wartet man einfach eine kleine Zeit, bis sich der Eingang >nicht 
mehr ändert.
Auf keinen Fall mit "Wait" oder anderen Verzögerungen innerhalb der 
"LOOP", sondern indem man über den Timer eine Variable herunterzählt. 
Dazu legt man den eingelesenen Wert in einer Kontroll-Variablen ab und 
im nächsten Zyklus vergleicht man mit einem "EOR" den Status. Wenn noch 
ein Bit gesetzt ist, wird ein Wert in einer Zählvariablen gesetzt. Ist 
das Ergebnis der "EOR" Null, also kein Bit gesetzt, wird die 
Zählvariable heruntergezählt. Ist die Zählvariable Null, ist der Eingang 
als stabil zu betrachten und es kann geprüft werden, ob sich zum letzten 
stabilen Zustand etwas geändert hat. Wenn man einen Timer nimmt, wird 
die Zählvariable unabhängig vom Programmzyklus bearbeitet, man kann aber 
auch in den  Programmschleifen den Zähler bearbeiten und 5-10 Durchläufe 
auf stabile Lage der Eingänge warten.

Gruß Oldmax

von Holger S. (capa)


Lesenswert?

ich schau mir gerade den source von

http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten

an und dazu nen "Tutorial" was die internen timer angeht die bei dem 
source verwendet werden.

http://www.tschallener.net/AVR/timer0.pdf

war im ersten moment etwas verzweifelt als ich da TCNT0 etc gelesen 
hatte ^^ naja in den nächsten tagen kommt das steckbrett und ein paar 
bauteile und dann werd ich endlich mal anfangen können mit basteln und 
testen.

Den schliesslich ist es immer einfacher wenn man das zusammenspiel 
Hardware/software sieht um etwas besser zu verstehen, ein wenig 
rumtesten und man versteht alles viel schneller als trockene Lektüre ^^

von Holger S. (capa)


Lesenswert?

Ich hab mal wieder ein kleines Problem aber weniger mit tasten eher mit 
dem benutzen von pointern. Wie kann ich meinen jetzigen code so 
erweitern das er nach der 9 an dem ersten display wieder auf 0 geht und 
beim 2ten display mit 1 anfängt das display quasi erweitern (immo bis 
auf 4 displays ausdehnbar) nur weiß ich nicht wirklich wie ich das 
anfangen soll :/ unter ausgang sind die ausgänge für die latches die ich 
benutze. Aufbau immernoch wie in [1] beschrieben.

[1] Beitrag "Atmega32, 7segment-Anzeige, Portproblem"

[
1
/*
2
 * AVRAssemblerx.asm
3
 *
4
 *  Created: 02.02.2012 22:22:24
5
 *   Author: Capa
6
 */ 
7
8
.include "m32def.inc"
9
10
  // Variablen //
11
.def temp         = r16 
12
.def display      = r18
13
.def temp1        = r19
14
.def temp2        = r20
15
.def temp3        = r21
16
17
.equ segment_ddr  = ddrc
18
.equ segment_port = portc
19
.equ latch_ddr    = ddra
20
.equ latch_port   = porta
21
  // Variablen //
22
23
.cseg
24
.org 0
25
    rjmp reset
26
27
reset:
28
  // Stackpointer init //
29
    ldi temp, low(ramend)
30
    out spl, temp
31
    ldi temp, high(ramend)
32
    out sph, temp
33
  // Stackpointer init //
34
35
  // Z-Pointer //
36
    LDI ZH,HIGH(2*message)
37
    LDI ZL,LOW(2*message)
38
  // Z-Pointer //
39
40
  // Ausgänge einstellen //
41
    ldi temp, 0xFF
42
    out segment_ddr, temp
43
    ldi temp, 0xFF
44
    out latch_ddr, temp
45
  // Ausgänge einstellen //
46
47
endlos:
48
49
  lpm 
50
51
  tst r0
52
  breq quit
53
54
  ldi display, 0b11111111
55
  out latch_port, display
56
  out segment_port, r0
57
  ldi display, 0b11111110
58
  out latch_port, display
59
  rcall delay
60
61
  adiw ZL,1
62
    rjmp endlos
63
64
quit: rjmp quit
65
66
delay:
67
    ldi temp1, 0xFF
68
    ldi temp2, 0xFF
69
    ldi temp3, 10
70
delay_2:
71
    dec temp1
72
    brne delay_2
73
    dec temp2
74
    brne delay_2
75
    dec temp3
76
    brne delay_2
77
    ret
78
79
message:
80
  .db 0b11000000,0b11111001
81
  .db 0b10100100,0b10110000
82
  .db 0b10011001,0b10010010
83
  .db 0b10000011,0b11111000
84
  .db 0b10000000,0b10011000
85
86
ausgang:
87
  .db 0b11111110,0b11111100
88
  .db 0b11111000,0b11110000

von Karl H. (kbuchegg)


Lesenswert?

Holger Schreiber schrieb:
> Ich hab mal wieder ein kleines Problem aber weniger mit tasten eher mit
> dem benutzen von pointern. Wie kann ich meinen jetzigen code so
> erweitern das er nach der 9 an dem ersten display wieder auf 0 geht und
> beim 2ten display mit 1 anfängt das display quasi erweitern (immo bis
> auf 4 displays ausdehnbar) nur weiß ich nicht wirklich wie ich das
> anfangen soll :/ unter ausgang sind die ausgänge für die latches die ich
> benutze. Aufbau immernoch wie in [1] beschrieben.

Du solltest als erstes die eigentliche Zählerei von der Ausgabe (mittels 
des Z-Pointers) trennen.

Zum Zählen 0 bis 9 benutzt du eigene Register und erst wenn es zur 
Ausgabe kommt, zählst du den Zähler dann zu 2*MESSAGE (im Z-Pointer) 
dazu um das Byte zu erhalten, welches am Port ausgegeben werden muss.

So wie du das jetzt hast, ist das zwar eine nette 
Technologie-Demonstration zum Thema 'Ansteuerung von 7-Segment', aber du 
stehst in einer Sackgasse. So kommst du nicht weiter.

von Holger S. (capa)


Lesenswert?

wenn ich dich richtig verstehe müsste ich auch die daten im bereich 
ausgang mit Z abholen. Allerdings stellt sich mir dann die frage wenn 
ich die selbe "variable" nutze die dem pointer sagt an welcher stelle es 
weiter geht, dann müsste er ja wenn ich wieder zurückspringe dort 1 
hinzugefügt haben obwohl ich es nicht ausgegeben hab, oder ?

Also sprich wenn ich nach der 9 wechsel das ich meine daten vom Label 
ausgang hole müsste meiner meinung nach danach die 11 erscheinen und 
nicht die 10 oder seh ich das gerade falsch ?

---
Edit: Oder aber er geht auf ausgang und findet dort nix weil an stelle 9 
keine daten stehen.
---

Besteht da auch die möglichkeit die aktuelle position irgendwie zu 
speichern in einer anderen variable und die dann später wieder zu 
übernehmen ?

Gibt es vielleicht irgendwo ein kleines bsp was ich mir zum besseren 
verständniss anschauen könnte ? Soll jetzt nicht auf mein Problem 
bezogen sein, falls jemand denkt das ich hier fertigen code haben will 
um mir die arbeit zu erleichtern indem ich es einfach übernehme. Ich 
verstehe manche dinge einfacher wenn ich Codesegmente sehe.

Asm ist so schon eine schwere sprache gewesen für mich hatte mich mal 
versucht damit auseinanderzusetzen im zusammenhang mit delphi. 
Allerdings kam ich da nicht wirklich weiter daher interressiert es mich 
jetzt umso mehr ^^ schon alleine weil die programme dann viel kleiner 
sind vom speicher her als wenn ich andere sprachen nutze.

von Karl H. (kbuchegg)


Lesenswert?

schau dir das erste Beispiel hier an
http://www.mikrocontroller.net/articles/AVR-Tutorial:_7-Segment-Anzeige

hier ist der Counter von der 7-Seg Ansteuerung getrennt.

von oldmax (Gast)


Lesenswert?

Hi
Nun, auf einen Irrtum sollte ich hinweisen:
> schon alleine weil die programme dann viel kleiner
>sind vom speicher her als wenn ich andere sprachen nutze.
Ich glaube nicht, das ein Anfänger wirklich kleinere Programme schreibt, 
als ein Compiler einer Hochsprache. Nicht vergessen, auch Assembler wird 
compiliert. Sicherlich, ein ausgefuchster Programmierer wird einen 
kürzeren Code in einigen bereichen schreiben können, aber nur deshalb, 
weil er vielleicht auf Gültigkeitsroutinen verzichten kann, oder eben 
nicht ein universelles Zahlenformat benutzt, sondern nur die geforderte 
Genauigkeit implementiert. Aber das ist hier nebensächlich. Für die 
Anzeige von Zahlenwerten in 7 Segment Anzeigen werde ich dir mal meinen 
Ansatz mitteilen. Zuerst definiere ich immer für solche Zwecke 
Variablen. Das ist vielleicht etwas umständlich, aber so behälst du den 
Überblick.
Es gibt folgende Variablen:
1
Matrix_0:       .Byte 1
2
Matrix_1:       .Byte 1 usw. bis Matrix_9
3
;...
4
;---------------------------------
5
6
; nun folgen die Ziffern, Inhalt später Werte von 0 - 9
7
; entsprechend der Anzahl der angezeigten Ziffern
8
Ziffer_1:       .Byte 1    ; Inhalt Wert 0 -9
9
Ziffer_2:       .Byte 1
10
Ziffer_3:       .Byte 1 etc.
11
;---------------------------------
12
13
; Bei Multiplex ist es Sinnvoll, den Ziffern auch gleich
14
; den Segmentcode in eigenen Variablen abzulegen
15
16
Seg_Code_1:     .Byte 1   ; Inhalt der 7-Segmentcode der Ziffer
17
Seg_Code_2:     .Byte 1   
18
Seg_Code_3:     .Byte 1   ; etc
19
20
;---------------------------------
Nunkannst du in einer eigenen Routine zum Beispiel die Ziffern 
hochzählen. Dazu benutzt du Ereignisse. Z.B. einen Tastendruck.
Ich hatte es ja bereits geschrieben, das ein "KeyPressed" oder "KeyUp" 
als Ereignis relativ einfach umzusetzen ist.
Eine Routine von mir dazu ist auch nix besonderes:
1
;---------------------- Eingänge entprellen ----------------------------
2
;***********************************************************************
3
;* für 6 Eingänge werden bei Wechsel der Pegel eine Prellzeit gestartet  *
4
;***********************************************************************
5
IO_Debounce:          ; Prellzeit
6
  LDS  Reg_A, In_Debounce  ; Eingänge unter Kontrolle
7
  LDS  Reg_B, New_In    ; neue Eingänge
8
  EOR  Reg_A, Reg_B  
9
  BREQ  Deb_Time      ; keine Änderung mehr
10
  STS  In_Debounce, Reg_B         ; neue Eingänge ablegen
11
  LDI  Reg_B, 5                   ; 5 Timerereignisse warten
12
  STS  Deb_Time, Reg_B
13
  RJMP  End_Dbnc
14
Deb_Time:        ; Prellzeit abwarten
15
  LDS  Reg_A, Deb_Time
16
  CPI  Reg_A, 0
17
  BREQ  End_Dbnc      ; keine Prellzeit gesetzt
18
  LDS  Reg_C, Time_Flag          ; wird im Timer gesetzt
19
  ANDI  Reg_C, 0b00000010    ; Flag aus Timer bearbeiten
20
  BREQ  End_Dbnc
21
  LDS  Reg_C, Time_Flag           ; wenn gesetzt, dann
22
  ANDI  Reg_C, 0b11111101          ; Flag löschen
23
  STS  Time_Flag, Reg_C    ; und quittieren
24
  
25
  Dec   Reg_A                    ; Prellzeit herunterzählen
26
  STS  Deb_Time, Reg_A            ; und ablegen
27
  CPI  Reg_A, 0      ; Prellzeit abgelaufen ?
28
  BRNE  End_Dbnc
29
;-------------------- Flankenmerker 0->1 setzen --------------------
30
;                        Eingänge sind stabil
31
  MOV  Ablage_A, Reg_B            ; neue Eingänge merken
32
  LDS  Reg_A, Old_In    ; letzter stabiler Zustand
33
  MOV  Ablage_B, Reg_A            ; alte Eingänge merken
34
  EOR  Reg_A, Reg_B    ; geänderte Eingänge sind "1"
35
  MOV  Reg_B, Ablage_A    ; letzten gültigen Status
36
  AND  Reg_B, Reg_A               ; Ergebnis ist Wechsel 0->1
37
  LDS  Temp_Reg, Event_To_1       ; Ereignisbits "KeyPressed"
38
  OR  Reg_B, Temp_Reg            ; neue Änderung einfügen
39
  STS  Event_To_1, Reg_B    ; und ablegen
40
  MOV  Reg_B, Ablage_B            ; nun noch Wechsel 1->0
41
  AND  Reg_B, Reg_A
42
  LDS  Temp_Reg, Event_To_0       ; Ereignisflag KeyUp
43
  OR  Reg_B, Temp_Reg            ; neues Flag hinzufügen
44
  STS  Event_To_0, Reg_B          ; und ablegen
45
  MOV  Reg_B, Ablage_A       ; neue stabile Eingänge für
46
  STS  Old_In, Reg_B         ; nächste Flankenbildung merken
47
    
48
End_Dbnc:
49
RET
50
;--------------------------------------------------------------------
Nunkannst du die Flgs in "Event_to_1" oder "Event_to_0" für die Zählung 
nutzen.
1
Count_Up:
2
   LDS   Reg_A, Event_To_1     ; Ereignisbits laden
3
   ANDI  Reg_A, 0b00000001     ; Bit 0 (Taster) gesetzt
4
   BREQ  End_Count_UP          ; nee, also ende
5
   LDS   Reg_A, Event_To_1
6
   ANDI  Reg_A, 0b11111110     ; nur das Ereignisbit 0 löschen
7
   STS   Event_to_1, Reg_A
8
   LDS   Reg_A, Ziffer_1
9
   INC   Reg_A                 ; Ziffer 1 erhöhen
10
   STS   Ziffer_1, Reg_A       ; und ablegen
11
   CPI   Reg_A, 10             
12
   BRLO  End_Count_Up          ; Wert < 10, dann Ende
13
   CLR   Reg_A                 
14
   STS   Ziffer_1, Reg_A       ; Ziffer 1 =0
15
16
;--------------------------------------------------------
17
; Hier kann bereits der Code kopiert und angepasst werden
18
;--------------------------------------------------------
19
   LDS   Reg_A, Ziffer_2       ; gleicher Vorgang Ziffer 2
20
   INC   Reg_A                 ; Ziffer 2 erhöhen
21
   STS   Ziffer_2, Reg_A       ; und ablegen
22
   CPI   Reg_A, 10             
23
   BRLO  End_Count_Up          ; Wert < 10, dann Ende
24
   CLR   Reg_A                 
25
   STS   Ziffer_2, Reg_A       ; Ziffer 2 =0
26
;----------- erneute Kopie für Ziffer 3 ----------------
27
   LDS   Reg_A, Ziffer_3
28
   INC   Reg_A                 ; Ziffer 3 erhöhen
29
   STS   Ziffer_3, Reg_A       ; und ablegen
30
   CPI   Reg_A, 10             
31
   BRLO  End_Count_Up          ; Wert < 10, dann Ende
32
   CLR   Reg_A                 
33
   STS   Ziffer_3, Reg_A       ; Ziffer 3 =0
34
;----------------------------------------------------
35
;       eventuell weitere Ziffern zählen 
36
;----------------------------------------------------
37
End_Count_Down:
38
RET
Nun hab ich einen Counter. Ob nun gesetzte Flags aus dem Timer oder von 
einer Tasterabfrage kommen, der Vorgang ist immer der selbe. Damit 
kannst du munter experimentieren. Auch kannst du z.B. damit Zeichen 
zählen, die über die UART-ISR kommen. Einfach ein Bit in einer Variablen 
setzen und hier prüfen. Wenn gesetzt, das Bit quittieren und den Vorgang 
bearbeiten.
Nun fehlt noch die Übergabe einer Ziffer an eine 7-Segmentanzeige. Da 
die Ziffern völlig für sich allein stehen, kannst du auch hier eine 
völlig gelöste Unterroutine dafür benutzen. Zuerst setzt du den Zählwert 
in eine 7-Segment-Matix um und schreibst diese Werte in die 
Seg_Code-Variablen
Dazu benutzt du Pointerregister z.B. X, Y und Z
1
Set_Code_Ausgabe:
2
         CLR  Reg_A                 ; Register für Stellenzähler
3
         CLR  Reg_C                 ; Nullwert für Addition
4
     LDI  XL,LOW(Ziffer_1)  ; XPointer auf Zählerspeicher 
5
  LDI  XH,HIGH(Ziffer_1)
6
  LDI  ZL,LOW(Seg_Code_1)  ; ZPointer auf Anzeigespeicher 
7
  LDI  ZH,HIGH(Seg_Code_1)
8
Loop_Set_Code:
9
         LDI  YL,LOW(Matrix_0)  ; YPointer auf Segmentcode
10
  LDI  YH,HIGH(Matrix_0)
11
         LD   Reg_B, X+
12
         ADD  YL, Reg_B            ; Wert zum Segment
13
         ADC  YH, Reg_C
14
         LD   Reg_B, Y             ; Reg_B kann überschrieben werden
15
         ST   Z+, Reg_B            ; Code in Anzeigespeicher
16
         INC  Reg_A                ; Schleifenzähler erhöhen
17
         CPI   Reg_A, 3            ; z.B. für drei Ziffern
18
         BRLO  Loop_Set_Code       ; 
19
RET
Nun kannst du die Variablen "Seg_Code_1", "Seg_Code_2" und "Seg_Code_3" 
direkt den Ausgängen zuweisen. Allerdings sind dies schon 21 Portbits... 
Wenn du mehr Anzeigen brauchst, mußt du Multiplexen. Das heist, du 
schaltest alle Segmente parallel und steuerst den gemeinsamen immer nur 
auf eine Anzeige. Aber dazu findest du auch ein Tutorial. Die bisher 
beschriebene Vorgehensweise ist auch dafür geeignet. Ich steuer damit 
z.Zt. 12 Anzeigen. Und nicht nur mit Zahlen. Auch kleine Texte, 
z.B."PAUSE" ist kein Problem. Dazu brauchst du lediglich die Matrix 
erweitern und entsprechend initialisieren.
Gruß oldmax

von Karl H. (kbuchegg)


Lesenswert?

oldmax schrieb:
> Hi
> Nun, auf einen Irrtum sollte ich hinweisen:
>> schon alleine weil die programme dann viel kleiner
>>sind vom speicher her als wenn ich andere sprachen nutze.
> Ich glaube nicht, das ein Anfänger wirklich kleinere Programme schreibt,
> als ein Compiler einer Hochsprache. Nicht vergessen, auch Assembler wird
> compiliert. Sicherlich, ein ausgefuchster Programmierer wird einen
> kürzeren Code in einigen bereichen schreiben können, aber nur deshalb,
> weil er vielleicht auf Gültigkeitsroutinen verzichten kann, oder eben
> nicht ein universelles Zahlenformat benutzt, sondern nur die geforderte
> Genauigkeit implementiert. Aber das ist hier nebensächlich.

Geh ich völlig mit dir konform.
Vor allen Dingen ist er um einiges schneller in der Entwicklung.
Leute wie du oder auch spess53 outperformen einen Compiler relativ 
leicht. Allerdings nicht sehr viel. Und von der Entwicklungszeit seid 
ihr auch nicht viel langsamer, weil ihr genügend Projekte durchgezogen 
habt. Wenn es nur darum geht, ein einzelnes Projekt (oder ein paar) 
durchzuziehen, schlägt ein Assembler-Neuling einen Compiler nicht. Ganz 
im Gegenteil. Zusätzlich kommt noch dazu: die Fehleranfälligkeit und die 
Gefahr sich in eine Sackgasse zu manövrieren ist um einiges höher.

Ich find das ja auch lustig: auf der einen Seite steht die Angst nach 
zuviel Code, auf der anderen Seite steht dem ein Mega32 gegenüber.

von Holger S. (capa)


Lesenswert?

naja ATMega32 hab ich 3 hier ^^ (2x avr-netio und 1x zum 
evaluationsboard dazu gekauft)
hab mir erst vor kurzem 2 atmega8 zuschicken lassen aber für tests nehm 
ich trotzdem den 32er, ka warum gefällt mir irgendwie besser. :D 
Außerdem muss ich wenn ich die schaltung erweitern will und der atmega8 
nicht mehr ausreicht nicht extra alles neu bestücken und verdrahten

achja danke @ oldmax  werd mir nachher mal deinen code genauer anschauen 
bin gerade über dem bsp von Karl Heinz Buchegger, auch dir sei an dieser 
stelle gedankt :)

von Karl H. (kbuchegg)


Lesenswert?

Holger Schreiber schrieb:

> Außerdem muss ich wenn ich die schaltung erweitern will und der atmega8
> nicht mehr ausreicht nicht extra alles neu bestücken und verdrahten

Der Mega8 wird noch lange ausreichen. :-)

Du kommst von der PC Seite, daher ist das verständlich. Aber 8kB 
Programmspeicher ist auf µC Seite schon eine Menge Holz. Soviel kannst 
du an einer Dart Punkte-Anzeige gar nicht dazuerfinden, dass du die voll 
kriegen würdest.

von Holger S. (capa)


Lesenswert?

naja dafür werde ich dann wohl eher auf nen atmega8 umsteigen das stimmt 
aber ich meinte auch eher für meine testumgebung die sich ja auf mein 
steckbrett bezieht. Ich werde mit den mir zur verfügung stehenden 
mitteln eine menge rumbasteln und testen und das meinte ich damit. 
Vielleicht bau ich ja noch nen lcd mit in meine testumgebung :) oder ne 
reihe an tastern oder pack nen eeprom dazu usw. lieber nehm ich dafür 
einen MC und tue damit basteln und die schreibzyklen verbrauchen als mit 
3-4 unterschiedlichen MC's

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.