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
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.
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.
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.
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:
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).
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.
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
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...
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
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