Forum: Mikrocontroller und Digitale Elektronik 7 SEGMENT ANZEIGE


von opteronfx (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Leute ich hoffe ihr könnt mir helfen, stehe richtig auf dem 
schlauch. Aufgabestellung siehe Anhang.
Ich habe allerdings ein Problem mit dem Timer
Hier ist der Code:



stack_seg  SEGMENT IDATA    ; Deklaration Stack Segment
code_seg  SEGMENT CODE    ; Deklaration Code Segment

RSEG  stack_seg    ; Anfang des Stack Segmentes

Stack_data:
 DS  5      ; Für stack_data werden 5 Byte reserviert

 CSEG  AT 0      ; Programmbeginn bei Adresse NULL wird
 LJMP   init

 ORG 0x0B
 JMP ISR_tim0

 timer_high EQU 0x3C
 timer_low  EQU 0xAF



                         // Springt zu unserer Main, wo der Code 
gestartet wird

                          // Startadresse der 
siebenSegmentAnzeige_Tabelle, wo die Segmentsteuerung gespeichert ist.
Tabelle:                           // Tabellenlabel
db                    0x3F;        // Ansteuerung für   0
db                    0x06;        // Ansteuerung für   1
db                    0x5B;        // Ansteuerung für   2
db                    0x4F;        // Ansteuerung für   3
db                    0x66;        // Ansteuerung für   4
db                    0x6D;        // Ansteuerung für   5
db                    0x7D;        // Ansteuerung für   6
db                    0x07;        // Ansteuerung für   7
db                    0x7F;        // Ansteuerung für   8
db                    0x6F;        // Ansteuerung für   9
db                       0x3F;        // Ansteuerung für   A
db                       0x3F;        // Ansteuerung für   B
db                       0x3F;        // Ansteuerung für   C
db                       0x3F;        // Ansteuerung für   D
db                       0x3F;        // Ansteuerung für   E
db                       0x3F;        // Ansteuerung für   F


RSEG code_seg

init:

    MOV DPTR, #Tabelle
    MOV SP,  #Stack_data
    MOV TH0, #timer_high
    MOV  TL0, #timer_low
    MOV TMOD, #0x01
    SETB PT0
    SETB ET0
    SETB EA
    MOV P1, #0x00
    MOV P0, #0x00
    MOV P2, #0xFF
    MOV P3, #0x00
    MOV P2, #0x13
    MOV R3, #0x02
    MOV R4, #0x04

 Read:
   MOV A, P2      // Einlesen von Port 2 8 bit-Wert
       // Für Überprüfung
   MOV R1, A       // Wert von A Sichern in R1
   ANL A, #0FH   // Low-Teil (Bits: 0-3) ausmaskiert
   MOV R2, A     // LOw-Teil sichern
   MOV A, R1     // Originalwert nach A schreiben
   RRC A       // Der High-Teil (rotierbefehl, 4xMal zum maskieren)
   RRC A
   RRC A
   RRC A
   ANL A, #0FH    //        von A wird ausmaskiert
   sjmp CHECK

Label:
     MOV R1,A
    MOV A, R2    // Der Low-Teil wird nach A geschrieben

 CHECK:
   CJNE A, #9h, CHECK_LESS
  LJMP Berechnung

 CHECK_LESS:
   JC Berechnung

  EXCEPTION:
    MOV A, #0H



 Berechnung:
     MOV DPTR, #Tabelle
    MOVC A, @A+DPTR
    DJNZ R3,Label


Ausgabe:


       SETB TR0
       MOV P1, #02H
       MOV P0, R1
       MOV P1, #01H
       MOV P0, A
       CLR TR0
       JMP Ausgabe



ISR_tim0:         ; Interrupt  Routine
PUSH   ACC               ; Sichere Accu auf Stack
PUSH   PSW      ; Sichere ProgramStatusWord auf Stack
DEC   R4      ; R1 dekrementieren
MOV   TH0,#timer_high  ; Timer0 Register laden
MOV   TL0,#timer_low  ; Timer0 Register laden
POP   PSW                   ; ProgramStatusWord wird wiederhergestellt
POP  ACC                   ; Accu wird wiederhergestellt
RETI


end

von Alexander L. (lippi2000)


Lesenswert?

Hi,
also ohne genauen Einblick in den Intel-Assambler zu haben, sehe ich 
keinen Sinn in deinem Timer-Interrupt.

Was macht er? Zählt den anfänglichen Wert von R4=0x04 bis Null und 
anschließend durch Überlauf von 255 wieder nach Null.

Des Weiteren liest du nur einmal die Werte vom Port ein, deine 
Ausgabeschleife wird nie wieder verlassen. Ist das so gewollt?

Vom Prinzip würde ich den Timer es so machen:

ISR_tim0:         ; Interrupt  Routine
PUSH   ACC               ; Sichere Accu auf Stack
PUSH   PSW      ; Sichere ProgramStatusWord auf Stack

[Wenn aktuell Segment 1 dann springe zu Segment 2]
MOV P1, #01H
MOV P0, A
[Springe zu Ende_Segment:]

Segment2:
MOV P1, #02H
MOV P0, R1

Ende_Segment:


MOV   TH0,#timer_high  ; Timer0 Register laden
MOV   TL0,#timer_low  ; Timer0 Register laden
POP   PSW                   ; ProgramStatusWord wird wiederhergestellt
POP  ACC                   ; Accu wird wiederhergestellt
RETI


Jetzt hast du schon mal eine feste Frequenz für das Multiplexen.
Jetzt musst du nur noch zyklisch den Eingang prüfen und deine Berechnung 
starten. Entweder machst du dies in einer Endlosschleife im 
Hauptprogramm oder im Timer-ISR.

Ich hoffe es hilft dir weiter.

von opteronfx (Gast)


Lesenswert?

erst mal danke für deine antwort. die ausgabe schleife ist so gewollt. 
der teimer soll eigentlich das flimmern der anzeige beseitigen. Leider 
habe ich mit timer keine erfahrung. bin neu.
Danke für die hilfe.

von Sebastian (Gast)


Lesenswert?

Der Timer Interrupt muß etwas anderes machen: Er dient als Zeitbasis für 
die Umschaltung zwischen den beiden Ziffern.
Wenn die Anzeige zweistellig ist, steuert man entweder die Ziffer Nummer 
eins oder die Ziffer Nummer zwei an, möglichst abwechselnd und so, daß 
auf beide eine gleichlange Zeit entfällt. Dafür ist der Timer gut.

Man könnte im Timer-Interrupt eine Variable umschalten, die angibt, ob 
Ziffer 1 oder Ziffer 2 angesteuert ist. Im Hauptprogramm muß diese 
Variable kontinuierlich ausgewertet werden und entsprechend die Daten 
für Ziffer 1 oder Ziffer 2 auf dem Port ausgegeben.

Wie immer gilt: Es leuchten nicht beide Ziffern gleichzeitig. Durch 
schnelles Umschalten zwischen beiden erzeugt die Trägheit des Auges 
diesen Eindruck.

von Peter D. (peda)


Lesenswert?

Ehe man programmiert, schreibt man sich erstmal den PAP 
(Programmablaufplan) in Worten auf, z.B.:
1
Init:
2
  starte T0
3
while( 1 ){               // Main-Loop forever
4
  call wait
5
  Abfrage der 1. 4 Pins
6
  umwandeln in 7-Segment
7
  anzeigen Digit 1
8
9
  call wait
10
  Abfrage der 2. 4 Pins
11
  umwandeln in 7-Segment
12
  anzeigen Digit 2
13
}
14
15
wait:
16
  while( TF0 == 0 ){}    // warte, bis Bit gesetzt
17
  TF0 = 0                // Bit löschen
18
  T0 = Reload-Wert       // laden für 100Hz
19
  return


Peter

von Peter D. (peda)


Lesenswert?

opteronfx schrieb:
> MOV A, P2      // Einlesen von Port 2 8 bit-Wert
>        // Für Überprüfung
>    MOV R1, A       // Wert von A Sichern in R1
>    ANL A, #0FH   // Low-Teil (Bits: 0-3) ausmaskiert
>    MOV R2, A     // LOw-Teil sichern
>    MOV A, R1     // Originalwert nach A schreiben
>    RRC A       // Der High-Teil (rotierbefehl, 4xMal zum maskieren)
>    RRC A
>    RRC A
>    RRC A
>    ANL A, #0FH    //        von A wird ausmaskiert

Geht einfacher so:
1
        mov     a, P2
2
        mov     b, #16
3
        div     ab        ; A = high nibble, B = low nibble


Peter

von opteronfx (Gast)


Lesenswert?

Das ist ein guter tip. Danke. Also jungs ich komme mit meinem timer 
nicht weiter. wie würdet ihr das dann technisch machen?
Danke.

von Peter D. (peda)


Lesenswert?

opteronfx schrieb:
> Also jungs ich komme mit meinem timer
> nicht weiter.

Was am Timer ist denn das Problem?
Du mußt schon sagen, wo es klemmt.


Peter

von Wilhelm F. (Gast)


Lesenswert?

opteronfx schrieb:

> Das ist ein guter tip. Danke. Also jungs ich komme mit meinem timer
> nicht weiter. wie würdet ihr das dann technisch machen?

Bau dir im Timer-Interrupt eine State-Machine, die je Interrupt die 
Segmente nacheinander anspricht. Mit einer Variablen bzw. RAM-Byte extra 
dafür, was man für Verzweigungen dann dort im Interrupt benutzt. Du 
zählst sie in jedem Interrupt um 1 hoch, und verzweigst dann je nach 
Wert dorthin, wo du was machen möchtest.

So ein Unterscheidungsblock mit Verzweigungen, geht gut mit bedingten 
Sprungbefehlen (z.B. JZ, JNZ, CJNE).

von opteronfx (Gast)


Lesenswert?

Danke nochmals.
Also folgendes, ich versuche zwei bcd-werte auszugeben. Damit das 
flimmerfrei passiert sollte ein timer programmiert werden. Das ist genau 
das wo ich nicht mehr weiter komme. Von Timer habe ich einfach keine 
Ahnung.
Es wäre einfach nett, wenn ihr mal einen fertigen vorschlag zeigen 
würdet.
Ich bedanke mich für eure Mühe.

stack_seg  SEGMENT IDATA    ; Deklaration Stack Segment
code_seg  SEGMENT CODE    ; Deklaration Code Segment

RSEG  stack_seg    ; Anfang des Stack Segmentes

Stack_data:
 DS  5      ; Für stack_data werden 5 Byte reserviert

 CSEG  AT 0      ; Programmbeginn bei Adresse NULL wird
 LJMP   init

Tabelle:                           // Tabellenlabel
db                    0x3F;        // Ansteuerung für   0
db                    0x06;        // Ansteuerung für   1
db                    0x5B;        // Ansteuerung für   2
db                    0x4F;        // Ansteuerung für   3
db                    0x66;        // Ansteuerung für   4
db                    0x6D;        // Ansteuerung für   5
db                    0x7D;        // Ansteuerung für   6
db                    0x07;        // Ansteuerung für   7
db                    0x7F;        // Ansteuerung für   8
db                    0x6F;        // Ansteuerung für   9
db                       0x3F;        // Ansteuerung für   A
db                       0x3F;        // Ansteuerung für   B
db                       0x3F;        // Ansteuerung für   C
db                       0x3F;        // Ansteuerung für   D
db                       0x3F;        // Ansteuerung für   E
db                       0x3F;        // Ansteuerung für   F


RSEG code_seg

                MOV DPTR, #Tabelle
    MOV SP,    #Stack_data

    MOV P1, #0x00
    MOV P0, #0x00
    MOV P2, #0xFF
    MOV P3, #0x00
    MOV P2, #0x43
    MOV R3, #0x02

Read:
           mov     a, P2       // wert einlesen
                 mov     b, #16
                 div     ab          // A high und B lowbyte

sjmp CHECK

Label:
     MOV R1,A
    MOV A, B

CHECK:
   CJNE A, #9h, CHECK_LESS
  LJMP Berechnung

 CHECK_LESS:
   JC Berechnung

  EXCEPTION:
    MOV A, #0H



 Berechnung:
     MOV DPTR, #Tabelle
    MOVC A, @A+DPTR
    DJNZ R3,Label


Ausgabe:



       MOV P1, #02H
       MOV P0, R1
       MOV P1, #01H
       MOV P0, A
       JMP Ausgabe

end

Soweit bin ich gekommen, aber die anzeige muss mindestens 100 mal in die 
sekunde dargestellt werden, damits flimmerfrei ist. Und das ist genau 
mein problem. Ich weiss einfach nicht wie man eine timer_isr schreibt 
und wo man das im programm einsetzen soll.

von opteronfx (Gast)


Lesenswert?

ich habe versucht hier etwas zu basteln, bin am verzweifeln


stack_seg  SEGMENT IDATA    ; Deklaration Stack Segment
code_seg  SEGMENT CODE    ; Deklaration Code Segment

RSEG  stack_seg    ; Anfang des Stack Segmentes

Stack_data:
 DS  5      ; Für stack_data werden 5 Byte reserviert

 CSEG  AT 0      ; Programmbeginn bei Adresse NULL wird
 LJMP   init

 ORG 0x0B
 JMP ISR_tim0

 timer_high EQU 0x3C
 timer_low  EQU 0xAF



                         // Springt zu unserer Main, wo der Code 
gestartet wird

                          // Startadresse der 
siebenSegmentAnzeige_Tabelle, wo die Segmentsteuerung gespeichert ist.
Tabelle:                           // Tabellenlabel
db                    0x3F;        // Ansteuerung für   0
db                    0x06;        // Ansteuerung für   1
db                    0x5B;        // Ansteuerung für   2
db                    0x4F;        // Ansteuerung für   3
db                    0x66;        // Ansteuerung für   4
db                    0x6D;        // Ansteuerung für   5
db                    0x7D;        // Ansteuerung für   6
db                    0x07;        // Ansteuerung für   7
db                    0x7F;        // Ansteuerung für   8
db                    0x6F;        // Ansteuerung für   9
db                       0x3F;        // Ansteuerung für   A
db                       0x3F;        // Ansteuerung für   B
db                       0x3F;        // Ansteuerung für   C
db                       0x3F;        // Ansteuerung für   D
db                       0x3F;        // Ansteuerung für   E
db                       0x3F;        // Ansteuerung für   F


RSEG code_seg

init:

    MOV DPTR, #Tabelle
    MOV SP,    #Stack_data
    MOV TH0, #timer_high
    MOV  TL0, #timer_low
    MOV TMOD, #0x01
    SETB PT0
    SETB ET0
    SETB EA
    MOV P1, #0x00
    MOV P0, #0x00
    MOV P2, #0xFF
    MOV P3, #0x00
    MOV P2, #0x43
    MOV R3, #0x02
    MOV R2, #0x02


 Read:
     mov     a, P2
        mov     b, #16
        div     ab

      sjmp CHECK


 CHECK:
   CJNE A, #9h, CHECK_LESS
  LJMP Berechnung

 CHECK_LESS:
   JC Berechnung

  EXCEPTION:
    MOV A, #0H



 Berechnung:
     MOV DPTR, #Tabelle
    MOVC a, @a+DPTR
    MOV R3, a
    mov a, b
    movc a, @a+dptr
    mov b, a     // b low
    mov a, r3   // a high



main_loop:
       setb tr0
    segment_1:
         call warte100ml
         MOV P1, #02H
            MOV P0, a

    segment_2:
            call warte100ml
            MOV P1, #01H
              MOV P0, b
           jmp main_loop

warte100ml:

  MOV   A,R2      ; Lade R2 in den Accu
  JZ   label        ; Springe wenn Accu==0 zum Segment:
  JMP    warte100ml    ; Warte so lange bis ca. 200msec gewartet wurden.

label:
        CLR   TR0
      MOV   R2,#0x02
      mov a, r3   // a high-wert wiederherstellen
      reti


ISR_tim0:         ; Interrupt  Routine
PUSH   ACC               ; Sichere Accu auf Stack
PUSH   PSW      ; Sichere ProgramStatusWord auf Stack
DEC   R2      ; R4 dekrementieren
MOV   TH0,#timer_high  ; Timer0 Register laden
MOV   TL0,#timer_low  ; Timer0 Register laden
POP   PSW                   ; ProgramStatusWord wird wiederhergestellt
POP  ACC                   ; Accu wird wiederhergestellt
reti


end

von Wilhelm F. (Gast)


Lesenswert?

Mach im Timerinterrupt mal sowas:

Definiere vorher mal ein Zählbyte SEGMENTZAEHLER irgendwo im 
Datenspeichersegment.

Es ist nur eine grobe Idee, da ich schon mal in Assembler mit dem 8051 
arbeitete.


    cjne SEGMENTZAEHLER, #0, T0_INT_LABEL1
    sjmp Segment_Multiplex_1
T0_INT_LABEL1
    cjne SEGMENTZAEHLER, #1, T0_INT_LABEL2
    sjmp Segment_Multiplex_2
T0_INT_LABEL2
    cjne SEGMENTZAEHLER, #2, T0_INT_LABEL3
    sjmp Segment_Multiplex_3
T0_INT_LABEL3
    sjmp Segment_Multiplex_4

Segment_Multiplex_1
...  Verarbeitung Segment 1
    sjmp TO_RETI

Segment_Multiplex_2
...  Verarbeitung Segment 2
    sjmp TO_RETI

Segment_Multiplex_3
...  Verarbeitung Segment 3
    sjmp TO_RETI

Segment_Multiplex_4
...  Verarbeitung Segment 4
    sjmp TO_RETI


TO_RETI
    INC SEGMENTZAEHLER     ; State Variable
    ANL SEGMENTZAEHLER, #3 ; nur 4 Segmente
    RETI

Jedesmal, wenn ein Interrupt erfolgt, geht die Sache (SEGMENTZAEHLER) 
einen Schritt weiter. Nach der höchsten Stelle muß wieder bei Null 
begonnen werden. Vielleicht führt es gedanklich in die richtige 
Richtung...

von Peter D. (peda)


Lesenswert?

War mein PAP wirklich so unverständlich?
1
init:
2
        mov     tmod, #01h              ; T0 = 16bit
3
        setb    tr0                     ; run T0
4
main:
5
        mov     a, p2
6
        mov     b, #16
7
        div     ab                      ; quotient = high nibble
8
        call    bcd_to_7s
9
        call    wait
10
        setb    p1.0                    ; digit 0 off
11
        mov     p0, a                   ; high digit
12
        clr     p1.0                    ; digit 1 on
13
14
        mov     a, b                    ; remainder = low nibble
15
        call    bcd_to_7s
16
        call    wait
17
        setb    p1.0                    ; digit 1 off
18
        mov     p0, a                   ; low digit
19
        clr     p1.1                    ; digit 0 on
20
        jmp     main
21
22
wait:
23
        jbc     tf0, _wait1             ; wait until overflow
24
        jmp     wait
25
_wait1:
26
        mov     th0, #high(-5000)       ; 1MHz / 200Hz = 5000
27
        mov     tl0, #low(-5000)
28
        ret
29
30
bcd_to_7s:
31
        inc     a                       ; skip 'ret'
32
        movc    a, @a+pc
33
        ret
34
        db      3fh, 06h, 5bh, 4fh, 66h, 6dh, 7dh, 07h, 7fh, 6fh
35
        db      10h, 10h, 10h, 10h, 10h, 10h    ; non valid digits = '_'
36
end


Peter

von opteronfx (Gast)


Lesenswert?

Hallo Peter erst mal danke. ich wollte nur mal fragen was an meinem 
program falsch ist. nochmals vielen dank und einen schönen tag noch. lg, 
aus liederbach

von Peter D. (peda)


Lesenswert?

opteronfx schrieb:
> ich wollte nur mal fragen was an meinem
> program falsch ist.

Du hältst den Timer an, startest ihn aber nicht wieder.

Und 2 * 200ms = 2,5Hz, das ist etwas langsam.

Du liest nur einmal den P2 ein, ist das so gewünscht?

Benutze für Quelltext die Formatierungen des Forums, dann ist das 
deutlich übersichtlicher.


Peter

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.