Assembler Uhr mit Sekundenanzeige

...bin zu dem Schluss gekommen dass ich ganz dringend ne Codetabelle mit ausführlicher Erklärung zu allen Assembler Befehlen brauch...
Schon mal auf die Idee gekommen, bei Atmel danach zu suchen?
Da steht dann auch, wie der entsprechende Befehl (so zB CP)das Statusregister (SREG) manipuliert.
Die folgenden BRxx sind bedingte Sprungbefehle (branch if xx). Die tun genau das (springen/verzweigen), eben in Abhängigkeit der jeweiligen bits in SREG.
Noch ein Hinweis am Rande: man muß nicht immer vergleichen, für den BRxx - Recheninstruktionen beeinflussen das SREG ja auch...

Welche ASM-Instruktionen der verwendete Controller konkret unterstützt, steht (wie Oldmax bereits sagte) in dem jeweiligen Controller-Datenblättchen (irgendwo hinten - Instruction summary oder so)...

Noch was für "fortgeschrittenere ASMler": anhand der Opcodes kann man sehen, daß einige verschiedene Mnemonics zu exakt demselben Code führen (BRCC=BRSH, BRCS=BRLO) - da kann man dann verwenden, was in der jeweiligen Situation "besser klingt" (also zb ob das carry aus einem "Vergleich", oder aus einer "Rechenoperation" (Überlauf) kommt).
 
Hallo,

also die Befehlsübersicht ist als PDF bei Atmel zu finden (doc0856.pdf - 8-bit AVR Instruction Set). Zieh dir die PDF direkt von Atmel runter um die neueste zu bekommen. Also nicht mit Google irgendwo anders total veraltete Versionen holen.

In Assembler hast du keine Variablen im Sinne der Hochsprachen. Du machst alle Operationen über die Register und lagerst Werte in SRAM-Zellen aus oder holst dir Konstanten aus dem Flash. Alle Ergebnisse eines Vergleichs, Addition, Subtraktion, Pintest, ... findest du im Statusregister (den Flags). Wenn ein Ergebnis Null war ist das Zero-Flag gesetzt. Bei negativem Ergebnis ist das Sign-Flag gesetzt. Wenn das Ergebnis nicht mehr in 8-Bit gepaßt hat ist ein Überlauf passiert und du findest das 9te Bit im Carry-Flag, usw.

Assembler ist sehr logisch und kompakt aufgebaut. Es ist aber erst einmal Umdenken gegenüber einer Hochsprache angesagt. Danach wirst du dich wundern warum man in einer Hochsprache für einfache Lösungen solche Klimmzüge machen muß.

Gruß
Dino
 
Hallo

So, ich hab jetzt mal ne weile rum experimentirt. wegen der Anzeige hab ich mir überlegt lieber ne LCD Anzeige zu benutzen (das Wissen hab ich mir bereits angeeignet) xD

Ich hab schonmal mit dem Programm angefangen. Die LCD Routine funktioniert schonmal einwandfrei. genau so wie der Timer schon in einem anderen versuch funktioniert hat. aber sobald man alles zusammen würfelt funktioniert es iwi nichtmehr und das Display zeigt garnichts an o_O

Der Timer sollte nach einer (momentan nicht definierten Zeit) das Register "Sekunde" immer um eins erhöhen. dann wird das Register mit 60 verglichen. falls das Register 60 erreicht hat erfolgt ein Sprung zu "Minute" dortz wird das Sekundenregister gelöscht und Minutenregister um eins erhöht. dann erfolgt wider die Abfrage auf 60. falls zb. Bei Sekunde die Zahl 60 nicht erreicht wurde springt das Programm zu "menue" dort wird aus der Zahl im Register (zB.25) die einzelnen Zahlen 2 und 5 nacheinander am Display ausgegeben. (Momentan gibz nur die Sekundenanzeige) und nichtmal die funktioniert :stupid:
Hab ich da etwas übersehen, oder ist die Richtung schonmal richtig? -> Ich hab nen Verdacht dass die Abfrage mit "BRLO" iwi probleme macht. Aber ich finde den Fehler einfach nicht.

Hätte jemand einen Vorschlag?
Gruß


Code:
;************************************************
; Bei Taktänderung (Quarz) nicht vergessen die 	;
; Wartezeiten und nop Befehle an zu passen	;
; nop und Wartebefehle sind in diesem Skript	; 
;willkürlich gewählt und performence orientiert.;
;						;
; Chip: ATmega8					;
; BD0 = PORTC,0					;
; BD1 = PORTC,1					;
; BD2 = PORTC,2					;
; BD3 = PORTC,3					;
; RS  = PORTD,4					;
; RW  = GND					;
; E   = PORTD,5					;
;						;
;************************************************


.include "m8def.inc"
 
;Alias Namen erstellen
.def temp1 = r16
.def temp2 = r17
.def temp3 = r18
.def temp4 = r19

.def Sekunde = r20
.def Minute  = r21
.def Stunde  = r22

LDI	temp1,60
mov	r2,temp1
LDI	temp1,24
mov	r3,temp1
 
ldi temp1, LOW(RAMEND)  
out SPL, temp1
ldi temp1, HIGH(RAMEND)  
out SPH, temp1

;Timer Initialisieren

.org	OVF0addr
	rjmp	Timer0_Overflow
	LDI		temp1,(1<<cs00)	| (1<<cs02)		;Teiler=1024
	out		TCCR0,temp1
	LDI		temp1,(1<<TOIE0)
	out		TIMSK,temp1
	sei

Start:
;****************************************************
	LDI		temp1,0b11111111
	Out		DDRD,temp1
	LDI		temp1,0b11111111
	out		DDRC,temp1
	LDI		temp1,0b11111111
	out		DDRB,temp1

	rcall	Display_ini
	rcall	Ende
;Hauptmenü
;****************************************************
Menue:
	
rcall	LCD_löschen




;Zahl schreiben (Sek)
	push	temp1
	push	temp2				;Temp1,2, Sichern

	mov		temp1,Sekunde		;Temp1,2 mit der Sekundenzahl laden
	clr		temp2
			
;Zehner
	ldi		temp2,'0'-1
LCD_Nummer5:
	inc		temp2
	subi	temp1,10
	brcc	LCD_Nummer5
	subi	temp1,-10
	rcall	Print

;Einer
	mov		temp2,temp1
	rcall	Print
	
	pop		temp2				;Temp1,2 wider beschreiben
	pop		temp1


Ende:
	rjmp	Ende

;			Unterprogramme
;-----------------------------------------------------------------



;Timer Overflow
;************************************************

Timer0_Overflow:
	INC		Sekunde
	CP		Sekunde,r2 ;Sekunde mit 60 vergleichen
	BRLO	Menue		
	rjmp	Menue

Minute:
	CLR		Sekunde
	INC		Minute
	CP		Minute,r2	;Minute mit 60 vergleichen
	BRLO	Stunde
	rjmp	Menue

Stunde:
	CLR		Minute
	INC		Stunde
	CP		Stunde,r3	;Stunde mit 24 vergleichen
	BRLO	Neuer_tag
	rjmp 	Menue

Neuer_tag:
	CLR		Sekunde		;Die Uhr auf null Setzen
	CLR		Minute
	CLR		Stunde
	rjmp 	Menue

; Display Installieren
;**************************************************

Display_ini:
	rcall	Waitms20
	ldi		temp3,3
Widerholung:
	LDI		temp2,0b00000011
	Out		portd,temp2
	rcall	send
	dec		temp3
	brne	Widerholung
	LDI		temp2,0b00000010
	OUT		Portd,temp2
	rcall	send
	LDI		temp2,0b00101000
	rcall	Send_Befehl
	LDI		temp2,0b00001100
	rcall	Send_Befehl
	LDI		temp2,0b00000100
	rcall	Send_Befehl

	ret


;LCD Löschen
;*************************************************

LCD_Löschen:
	ldi		temp2,0b00000001
	rcall	Send_Befehl
	ret



;Aufs LCD schreiben
;*************************************************

Print:
	Mov		temp3,temp2
	swap	temp2
	andi	temp2,0b00001111
	sbr		temp2,0b00010000
	out		PortD,temp2
	rcall	send
	andi	temp3,0b00001111
	sbr		temp3,0b00010000
	OUT		Portd,temp3
	rcall	Send
	ret

;Instruktionen ans LCD
;*************************************************

Send_Befehl:
	Mov		temp3,temp2
	swap	temp2
	andi	temp2,0b00001111
	out		PortD,temp2
	rcall	send
	andi	temp3,0b00001111
	OUT		Portd,temp3
	rcall	Send
	ret	


;Warteschleifen
;************************************************
Waitms5:
	LDI		temp1,50
Waitms5_1:
	LDI		temp2,255
Waitms5_2:
	dec		temp2
	brne	waitms5_2
	dec		temp1
	brne	Waitms5_1
	ret
	


Waitms20:
	LDI		temp1,200
Waitms20_1:
	LDI		temp2,255
Waitms20_2:
	dec		temp2
	brne	waitms20_2
	dec		temp1
	brne	Waitms20_1
	ret

Waitms1000:
	LDI		temp1,150
Waitms1000_1:
	LDI		temp2,255
Waitms1000_2:
	LDI		temp3,255
Waitms1000_3:
	dec		temp3
	brne	Waitms1000_3
	dec		temp2
	brne	Waitms1000_2
	dec		temp1
	brne	Waitms1000_1
	ret
	


;Sendefreigabe
;************************************************
Send:
	SBI		portd,5
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	CBI		portd,5
	rcall	waitms5
	ret
 
Hätte jemand einen Vorschlag?

Hallo,

ich habe mir dein Programm nicht komplett angesehen, mir ist aber einiges ausfgefallen

(1) Du nutzt den TimerOverflow Interrupt des Timer0. Den Einsprung zur ISR hast du richtig definiert. Dein Programmstart (ab Adresse 0) könnte allerings durch rjmp Timer0_Overflow überschrieben werden, hier müsste der Assembler dann warnen. Wenn ausreichen Platz ist, dann nicht, allerdings läuft der Programmcounter in dieses rjmp rein. Die ISR wird also angesprungen.

Du müsstest hier in etwa so vorgehen:

Code:
.org 0
rjmp ProgrammStart

.org yxz
rjmp Interruptserviceroutine
...

ProgrammStart:
; Stack initialisieren
; sonstige Sachen initialisieren

MainLoop:
...
rjmp MainLoop


(2) Ich finde in deinem Programm kein "reti" ReturnFromInterrupt.

(3) Fast das ganze Programm, bis ausser Initialisierungen, läuft in der ISR des Timerinterurpts. Das sollte man vermeiden. Da du die ISR nicht mit reti beendest, sind Interrupts global deaktiviert und es passiert nichts mehr.

Das Problem ist nicht das eine BRLO, sondern die Struktur des Programms.

Noch ein Hinweis: Du verwendest alias von Registern (Minute, Sekunde) auch als Label, hier müsste der Assembler eigentlich einen Fehler melden. Du verwendest auch Umlaute in Labeln. Möglicherweise hast du aber einen anderen Assembler als ich, der dieses toleriert, meiner mag das nicht und meldet Fehler.

Gruß,
Dirk
 
Hallo Dirk

Ich benutze AVR STudio4 das kann mit ÖÜÄ eigendlich gut umgegen.

Ich hab die ganzen initialisierungen usw mal neu angeordnet. wie du gesagt hast. zudem hab ich rein interessehalber das ganze Timer Zeugs raus genommen. Und jetzt hab ich das Problem gefunden.
Erklärung:
im Register "sekunde" steht zb. 82
dann wird die Zahl so zerlegt dass einmal 8 und 2 ans Display ausgegeben wird.
Dass es keine 82 Sekunden gibt ist mir auch klar. aber es dient ja nur zu testzwecken.
auf jeden Fall gibt das Display die erste Zahl (8) so aus wie es soll. die zweite Zahl wird jedoch nicht ausgegeben. ich denke es liegt irgendwo bei der Rechnerei wo das Register unter 0 rutscht (wobei es ja beim Zählregister (temp2) auch klapt o_O
Gruß

Code:
	CLR	temp1
	CLR	temp2
	CLR	temp3
	CLR	temp4
	LDI	Sekunde,82
;	rcall	Ende
;Hauptmenü
;****************************************************
Menue:
	
rcall	LCD_löschen


;Zahl schreiben (Sek)
	push	temp1
	push	temp2				;Temp1,2, Sichern

	mov		temp1,Sekunde		;Temp1 mit der Sekundenzahl laden
	clr		temp2
			
;Zehner
	ldi		temp2,'0'-1

LCD_Nummer5:
	inc		temp2
	subi	temp1,10
	brcc	LCD_Nummer5
	rcall	Print           <------------- Bis Hierher funktioniert alles ganz so wie es soll

;Einer
	subi	temp1,-10     <--------- Fehlerquelle!?
	mov		temp2,temp1
	rcall	Print
	
	pop		temp2				;Temp1,2 wider beschreiben
	pop		temp1
;	reti

Ende:
	rjmp	Ende
 
Hallo,

es wäre jetzt interessant nochmal dein ganzes Programm zu sehen, aber erst zu deinem Anzeigeproblem mit den Einern.

So wie ich dein Programm verstehe gibt "Print" den Inhalt von temp2 aus. Der Inhalt muss im ASCII Code sein, das heißt es ist der Offset '0' vorhanden.
Die Sekunden befinden sich in "Sekunde". Diese sind aber nicht in ASCII, sondern binär?!

Wenn du die Einer ausgeben möchtest kopierst du den Rest von temp1 (hier sind die 10er abgespaltet) nach temp2 und gibst temp2 mit "Print" aus.
Bei temp1 fehlt der Offset '0' für ASCII, da temp1 ja von "Sekunde" stammt und die ist binär.


Ich bin mir nicht hundertprozentig sicher, ob ich dein Programm so richtig verstanden habe. Ich vermute aber, hier liegt der fehler bei der Ausgabe.

Gruß,
Dirk
 
Hallo

Der Wert in "Sekunde" wird normalerweise mit "inc Sekunde" aufgefüllt. In dem Fall hab ich die Sekunde einfach mit "LDI sek,82" aufgefüllt.

Print gibt den Wert in temp2 aus.
Beispiel:
Code:
	LDI		temp2, '1'
	rcall	print

	;oder auch

	LDI		temp2, 'L'
	rcall	print

Das mit dem "ASCII" könnte stimmen, da der Wert in temp2 durch die INC Befehle aufgefüllt wird. im gegensatz dazu ist die zwei die am Ende in temp1 über bleibt das was ich vorhin eingegeben habe. kann man den Binärcode irgendwie umwandeln?

Hier einbmal die zerlegte "Uhren"version
(Timer wurde ausgeklammert)
Gruß
Code:
.include "m8def.inc"
 
;Alias Namen erstellen
.def temp1 = r16
.def temp2 = r17
.def temp3 = r18
.def temp4 = r19

.def Sekunde = r20
.def Minute  = r21
.def Stunde  = r22


.org	0
	rjmp	Programmstart

;.org	OVF0addr
;	rjmp	Timer0_Overflow


Programmstart:
;Timer Initialisieren
;	LDI		temp1,(1<<cs00)	| (1<<cs02)		;Teiler=1024
;	out		TCCR0,temp1
;	LDI		temp1,(1<<TOIE0)
;	out		TIMSK,temp1
;	sei


;Ausgänge definieren
	LDI		temp1,0b11111111
	Out		DDRD,temp1
	LDI		temp1,0b11111111
	out		DDRC,temp1
	LDI		temp1,0b11111111
	out		DDRB,temp1

;Stack Initialisieren 
	ldi temp1, LOW(RAMEND)  
	out SPL, temp1
	ldi temp1, HIGH(RAMEND)  
	out SPH, temp1

;Display Initialisieren
	rcall	Display_ini




;****************************************************



;Untere Register laden
	LDI	temp1,60
	mov	r2,temp1
	LDI	temp1,24
	mov	r3,temp1

	CLR	temp1
	CLR	temp2
	CLR	temp3
	CLR	temp4
	LDI	Sekunde,82
;	rcall	Ende
;Hauptmenü
;****************************************************
Menue:
	
rcall	LCD_löschen


;Zahl schreiben (Sek)
	push	temp1
	push	temp2				;Temp1,2, Sichern

	mov		temp1,Sekunde		;Temp1 mit der Sekundenzahl laden
	clr		temp2
			
;Zehner
	ldi		temp2,'0'-1

LCD_Nummer5:
	inc		temp2
	subi	temp1,10
	brcc	LCD_Nummer5
	rcall	Print

;Einer
	subi	temp1,-10
	mov		temp2,temp1
	rcall	Print
	
	pop		temp2				;Temp1,2 wider beschreiben
	pop		temp1
;	reti

Ende:
	rjmp	Ende

;			Unterprogramme
;-----------------------------------------------------------------



;Timer Overflow
;************************************************

Timer0_Overflow:
	INC		Sekunde
	CP		Sekunde,r2 ;Sekunde mit 60 vergleichen
	BRLO	Menue		
	rjmp	Menue

Minute:
	CLR		Sekunde
	INC		Minute
	CP		Minute,r2	;Minute mit 60 vergleichen
	BRLO	Stunde
	rjmp	Menue

Stunde:
	CLR		Minute
	INC		Stunde
	CP		Stunde,r3	;Stunde mit 24 vergleichen
	BRLO	Neuer_tag
	rjmp 	Menue

Neuer_tag:
	CLR		Sekunde		;Die Uhr auf null Setzen
	CLR		Minute
	CLR		Stunde
	rjmp 	Menue

; Display Installieren
;**************************************************

Display_ini:
	rcall	Waitms20
	ldi		temp3,3
Widerholung:
	LDI		temp2,0b00000011
	Out		portd,temp2
	rcall	send
	dec		temp3
	brne	Widerholung
	LDI		temp2,0b00000010
	OUT		Portd,temp2
	rcall	send
	LDI		temp2,0b00101000
	rcall	Send_Befehl
	LDI		temp2,0b00001100
	rcall	Send_Befehl
	LDI		temp2,0b00000100
	rcall	Send_Befehl

	ret


;LCD Löschen
;*************************************************

LCD_Löschen:
	ldi		temp2,0b00000001
	rcall	Send_Befehl
	ret



;Aufs LCD schreiben
;*************************************************

Print:
	Mov		temp3,temp2
	swap	temp2
	andi	temp2,0b00001111
	sbr		temp2,0b00010000
	out		PortD,temp2
	rcall	send
	andi	temp3,0b00001111
	sbr		temp3,0b00010000
	OUT		Portd,temp3
	rcall	Send
	ret

;Instruktionen ans LCD
;*************************************************

Send_Befehl:
	Mov		temp3,temp2
	swap	temp2
	andi	temp2,0b00001111
	out		PortD,temp2
	rcall	send
	andi	temp3,0b00001111
	OUT		Portd,temp3
	rcall	Send
	ret	


;Warteschleifen
;************************************************
Waitms5:
	LDI		temp1,50
Waitms5_1:
	LDI		temp2,255
Waitms5_2:
	dec		temp2
	brne	waitms5_2
	dec		temp1
	brne	Waitms5_1
	ret
	


Waitms20:
	LDI		temp1,200
Waitms20_1:
	LDI		temp2,255
Waitms20_2:
	dec		temp2
	brne	waitms20_2
	dec		temp1
	brne	Waitms20_1
	ret


;Sendefreigabe
;************************************************
Send:
	SBI		portd,5
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	CBI		portd,5
	rcall	waitms5
	ret
 
Ich würde sagen, bevor du die Einer mit "Print" ausgibst, addierst du '0' zu temp2 (bzw. 0x30 oder 48d).

Probiere das einfach erst mal aus.

Dirk
 
Hallo

Der Code siht jetzt so aus:
Code:
	LDI	Sekunde,85
;	rcall	Ende
;Hauptmenü
;****************************************************
Menue:
	
rcall	LCD_löschen


;Zahl schreiben (Sek)
	push	temp1
	push	temp2				;Temp1,2, Sichern

	mov		temp1,Sekunde		;Temp1 mit der Sekundenzahl laden
	clr		temp2
			
;Zehner
	ldi		temp2,'0'-1

LCD_Nummer5:
	inc		temp2
	subi	temp1,10
	brcc	LCD_Nummer5
	subi	temp1,-10
	rcall	Print
	
;Einer
LCD_Nummer6:
	mov 	temp2,temp1
	subi	temp2,'0'
	rcall	Print
		
	pop		temp2				;Temp1,2 wider beschreiben
	pop		temp1
;	reti

Ende:
	rjmp	Ende

Am Display rührt sich wenigsens schon mal was. jetzt wird 8E angezeigt. wobei es kein E ist sondern drei Striche unterienander und ein punkt dahinter.
es siht in etwa so aus:
___
___
___ .

Jedoch hab ich nicht '0' addiert sondern subtrahiert (subbi temp2,'0') sollte aber keinen Unterschied machen oder?
wenn ich stat '0' die Zahl '1' subtrahiere ändert sich das Zeichen. ich denke mal dass trotz allem irgendwas mit dem "subbi temp2,-10" nicht stimd. kann man überhaupt negative Zahlen subtrahieren? um diese zu addieren? bzw, gibt es keinen ordendlichen Additionsbefehl mit dem man konstante addieren kann?

Gruß
 
Hallo,
Jedoch hab ich nicht '0' addiert sondern subtrahiert (subbi temp2,'0') sollte aber keinen Unterschied machen oder?

addieren einer Konstanten (immediate) geht mit subtrahieren des Betrages mit neg. Vorzeichen.
(In deinem Programm hast du es ja schon gemacht beim Korrigieren der Einer: subi temp1, -10)

Also so:

subi temp2, -0x30


Du kannst es aber auch nicht immediate machen:

mov r16, temp2
ldi r17, 0x30
add r16, r17
mov temp2, r16

Das enspricht: temp2 = temp2 + 0x30
r16 und r17 müssen hier "frei" sein.
 
Ich dreh noch durch ^^
ich hab jetzt alles probiert was mir eingefallen ist und was so im Internet rum geistert. aber das dumme Display zeigt immer nur 8"E" oder 80 an -.-

Mein neuester Versuch (der wider mit der Zahl 80 gescheitert ist)
-> Der Zählvorgang für die Zehner Stelle wurde jetzt auch bei der EInerstelle angewandt. bringt aber nichts. es wird wider 80 stat 85 ausgegeben :/

Code:
	LDI	Sekunde,85
;	rcall	Ende
;Hauptmenü
;****************************************************
Menue:
	
rcall	LCD_löschen


;Zahl schreiben (Sek)
	push	temp1
	push	temp2				;Temp1,2, Sichern

	mov		temp1,Sekunde		;Temp1 mit der Sekundenzahl laden
	clr		temp2
			
;Zehner
	ldi		temp2,'0'-1

LCD_Nummer5:
	inc		temp2
	subi	temp1,10
	brcc	LCD_Nummer5
	subi	temp1,-10
	rcall	Print
	
;Einer
LCD_Nummer6:
	LDI		temp2,'0'-1
	inc		temp2
	subi	temp1,1
	brcc	LCD_Nummer6
	subi	temp1,-1

	rcall	Print
		
	pop		temp2				;Temp1,2 wider beschreiben
	pop		temp1
;	reti

Ende:
	rjmp	Ende
 
So, nach stunden langen rum spielen hats endlich geklapt!!! xD

dann sollte ja jetzt der Rest der Uhr hoffendlich kein großes Problem sein :)
Gruß

Code:
	LDI	Sekunde,71
;	rcall	Ende
;Hauptmenü
;****************************************************
Menue:
	
rcall	LCD_löschen


;Zahl schreiben (Sek)

	push	temp2				;Temp1,2, Sichern

	mov		temp1,Sekunde		;Temp1 mit der Sekundenzahl laden
	clr		temp2
			
;Zehner
	ldi		temp2,'0'-1

LCD_Nummer5:
	inc		temp2
	subi	temp1,10
	brcc	LCD_Nummer5
	subi	temp1,150	;<------ Hier liegt das Geheimnis! 150 abzihen, 
	push	temp1		;dann klapts neben ASCII auch mit Binärzahlen!?
	rcall	Print
	
;Einer
LCD_Nummer6:
	clr 	temp2
	pop		temp2
	subi	temp2,'0'
	rcall	Print		
	pop		temp2				;Temp1,2 wider beschreiben

;	reti

Ende:
	rjmp	Ende
 
Fast geschafft!

So, die Uhr ist fast fertig, alles funktioniert prima. (zwar über Umwege, aber Egal) ^^
Momentan läuft die Uhr jedoch noch viel zu schnell. das runterzählen mittels Register funktioniert leider nicht so wie gedacht. da muss ich mir wohl noch was anderes einfallen lassen :/

Hier einmal der Code zum durch schaun :)

Code:
;************************************************
; Bei Taktänderung (Quarz) nicht vergessen die 	;
; Wartezeiten und nop Befehle an zu passen	;
; nop und Wartebefehle sind in diesem Skript	; 
;willkürlich gewählt und performence orientiert.;
;						;
; Chip: ATmega8					;
; BD0 = PORTC,0					;
; BD1 = PORTC,1					;
; BD2 = PORTC,2					;
; BD3 = PORTC,3					;
; RS  = PORTD,4					;
; RW  = GND					;
; E   = PORTD,5					;
;						;
;************************************************


.include "m8def.inc"
 
;Alias Namen erstellen
.def temp1 = r16
.def temp2 = r17
.def temp3 = r18
.def temp4 = r19

.def Sekunde = r20
.def Minute  = r21
.def Stunde  = r22


.org	0
	rjmp	Programmstart



.org	OVF0addr
	rjmp	Timer0_overflow

Programmstart:



;Stack Initialisieren 
	ldi temp1, LOW(RAMEND)  
	out SPL, temp1
	ldi temp1, HIGH(RAMEND)  
	out SPH, temp1


;Ausgänge definieren
	LDI		temp1,0b11111111
	Out		DDRD,temp1
	LDI		temp1,0b00000000
	out		DDRC,temp1
	LDI		temp1,0b11111111
	out		DDRB,temp1

;Zeit laden
	LDI	Sekunde,0
	LDI	Minute,0
	LDI	Stunde,0

;Untere Register laden
	LDI	temp1,60
	mov	r2,temp1
	LDI	temp1,24
	mov	r3,temp1

;Display Initialisieren
	rcall	Display_ini

;Timer Initialisieren
	LDI		temp1,  (1<<cs02) | (1<<cs00)		;Teiler=1024
	out		TCCR0,temp1
	LDI		temp1,(1<<TOIE0)
	out		TIMSK,temp1
	sei




;	rcall	Timer0_Overflow

rcall	Loop


;Hauptmenü
;****************************************************





Menue:

rcall	LCD_löschen

;Stunden schreiben:
;*******************

	push	temp2				;Temp1,2, Sichern

	mov		temp1,Stunde		;Temp1 mit der Sekundenzahl laden
	clr		temp2
			
;Zehner
	ldi		temp2,'0'-1

LCD_Nummer1:
	inc		temp2
	subi	temp1,10
	brcc	LCD_Nummer1
	subi	temp1,150	;<------ Hier liegt das Geheimnis! 150 abzihen, 
	push	temp1		;dann klapts neben ASCII auch mit Binärzahlen!?
	rcall	Print
	
;Einer
LCD_Nummer2:
	clr 	temp2
	pop		temp2
	subi	temp2,'0'
	rcall	Print
	LDI		temp2,':'
	rcall	Print		


;Minuten schreiben
;******************
	
	mov		temp1,Minute		;Temp1 mit der Sekundenzahl laden
	clr		temp2
			
;Zehner
	ldi		temp2,'0'-1

LCD_Nummer3:
	inc		temp2
	subi	temp1,10
	brcc	LCD_Nummer3
	subi	temp1,150	;<------ Hier liegt das Geheimnis! 150 abzihen, 
	push	temp1		;dann klapts neben ASCII auch mit Binärzahlen!?
	rcall	Print
	
;Einer
LCD_Nummer4:
	clr 	temp2
	pop		temp2
	subi	temp2,'0'
	rcall	Print
	LDI		temp2,':'		
	rcall	Print

;Sekunden schreiben
;*******************



	mov		temp1,Sekunde		;Temp1 mit der Sekundenzahl laden
	clr		temp2
			
;Zehner
	ldi		temp2,'0'-1

LCD_Nummer5:
	inc		temp2
	subi	temp1,10
	brcc	LCD_Nummer5
	subi	temp1,150	;<------ Hier liegt das Geheimnis! 150 abzihen, 
	push	temp1		;dann klapts neben ASCII auch mit Binärzahlen!?
	rcall	Print
	
;Einer
LCD_Nummer6:
	clr 	temp2
	pop		temp2
	subi	temp2,'0'
	rcall	Print		
	pop		temp2				;Temp1,2 wider beschreiben

	reti

Loop:
	rjmp	Loop


;*************************************************************************************************************
Flug:
	rjmp	Menue

Timer0_overflow:
	INC		Sekunde
	CP		Sekunde,r2
	BRLO	Flug

Minute:
	CLR		Sekunde
	INC		Minute
	CP		Minute,r2
	BRLO	Flug

Stunde:
	CLR		Minute
	INC		Stunde
	CP		Stunde,r3
	BRLO	Flug

Neuer_Tag:
	CLR		Sekunde
	CLR		Minute
	CLR		Stunde
	rjmp	Flug


; Display Installieren
;*********************************************************************************************************************

Display_ini:
	rcall	Waitms20
	ldi		temp3,3
Widerholung:
	LDI		temp2,0b00000011
	Out		portd,temp2
	rcall	send
	dec		temp3
	brne	Widerholung
	LDI		temp2,0b00000010
	OUT		Portd,temp2
	rcall	send
	LDI		temp2,0b00101000
	rcall	Send_Befehl
	LDI		temp2,0b00001100
	rcall	Send_Befehl
	LDI		temp2,0b00000100
	rcall	Send_Befehl

	ret


;LCD Löschen
;*********************************

LCD_Löschen:
	ldi		temp2,0b00000001
	rcall	Send_Befehl
	ret



;Aufs LCD schreiben
;********************************

Print:
	Mov		temp3,temp2
	swap	temp2
	andi	temp2,0b00001111
	sbr		temp2,0b00010000
	out		PortD,temp2
	rcall	send
	andi	temp3,0b00001111
	sbr		temp3,0b00010000
	OUT		Portd,temp3
	rcall	Send
	ret

;Instruktionen ans LCD
;*************************************************

Send_Befehl:
	Mov		temp3,temp2
	swap	temp2
	andi	temp2,0b00001111
	out		PortD,temp2
	rcall	send
	andi	temp3,0b00001111
	OUT		Portd,temp3
	rcall	Send
	ret	


;Warteschleifen
;************************************************
Waitms5:
	LDI		temp1,50
Waitms5_1:
	LDI		temp2,255
Waitms5_2:
	dec		temp2
	brne	waitms5_2
	dec		temp1
	brne	Waitms5_1
	ret
	


Waitms20:
	LDI		temp1,200
Waitms20_1:
	LDI		temp2,255
Waitms20_2:
	dec		temp2
	brne	waitms20_2
	dec		temp1
	brne	Waitms20_1
	ret










;Sendefreigabe
;************************************************
Send:
	SBI		portd,5
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	CBI		portd,5
	rcall	waitms5
	ret
 
Hallo Folienkondensator,

du subtrahierst ja jetzt 150, damit funktioniert es. Ich habe mich erst gewundert woran das liegt, dann aber mal nachgerechnet ...

Hier also mal eine Erklärung zu dem

Code:
  subi    temp1,150    ;<------ Hier liegt das Geheimnis! 150 abzihen,
  push    temp1        ;dann klapts neben ASCII auch mit Binärzahlen!?



So müsste man es machen:
Wenn du die 10er abspaltest und diese Schleife ist fertig, muss nach dem bedingten Sprung (brcc) temp1 korrigiert werden, da ja einmal ein 10er zuviel abgezogen wurde. Hiermuss man dann wieder 10 addieren.
Das macht man mit subi temp1, -10.

Bevor man nun die Einer ausgibst, muss wegen ASCII korrigiert werden: subi temp2, -48 (bzw. -'0' oder -0x30). Ich denke, ich hatte dies bereits schon vor einigen Beiträgen erwähnt.

Man korrigierst also zweimal, insgesamt ist das nun +58 = +10 +48


Bei dir ist es so:
Du korrigierst einmal mit -150 und einmal mit -48 (subi temp2, '0'). Insgesamt sind das nun -198. Es gibt aber bei 8Bit einen negativen Überlauf: 256-198 = +58



Das bedeutet, du korrigierst ebenfalls mit +58, nur über einen Umweg und "versteckt", sicherlich hast du das über probieren herausbekommen.
Da das Programm nun so funktioniert, kannst du es auch so lassen, es ist nur schwerer "lesbar". Wenn du es aber korrigieren möchtest, probiere es mal im oberen Bereich mit
subi temp1, -10 ; 10 addieren
und um unteren Bereich mit
subi temp2, -48 ; 48 addieren (ASCII '0')

Gruß,
Dirk
 
Hi
Ihr wißt aber, was ihr da macht.....:eek: Sowas nenne ich "Chaos". (Er) Gefundene Werte abziehen und es klappt. Dies nachzuvollziehen ist fast unmöglich und sorry, auch nicht erforderlich. Ein Guter Programmierstil ist:
Sekunden zählen
Minuten zählen Stunden zählen
Was auch geht und fast besser, weil einfacher
Sekunden Einer
Sekunden Zehner
Minuten Einer
Minuten Zehner
Stunden Einer
Stunden Zehner
Die Visualisierung leitet man dann einfach von den Werten ab.
Sollen es ASCII Zeichen werden, dann eine "0" addieren. Eine "0" ist ASCII 0 und das bedeutet 30hex oder 48dez.
Soll es 7 Segment werden, einfach aus einer Matrix, die den 7 Segment.Code für die Ausgabe enthält über eine indirekte Adressierung mittels X,y oder Z-Register laden und ausgeben
.
Code:
   CLR    R1
   LDS    R16, Sek_Einer
; XPointer auf Matrix 7 Segmentcode

   LDI    XL,LOW(Matrix_0)		  
   LDI    XH,HIGH(Matrix_0)
; Zahlenwert zur Adresse der Matrix
   ADD   XL, R16                               
   ADC   XH, R1
   LD     R16, X                                 
; Ab jetzt ist der 7 Segmentcode für die Ausgabe in R16
So bleibt dein Code durchsichtig. Ehrlich gesagt, deinen Code durchzuarbeiten ist was für Masochisten. Warum bleibst du nicht in einer Linie..

Variablendeklaration

Überspringen der Interupt Vektor Tabelle
Initialisieren
Programmschleife
Eingänge lesen (fals erforderlich
Werte aus Zeitzähler zu 7 Segment oder ASCII konvertieren
Werte an Display liefern wenn ein LCD-Display
Zurück zur Programmschleife
Aufgabe des Timer Interrupt
Zeiten zählen
wenn 7Segmentanzeige den Multiplexer aufrufen und nur die konvertierten Werte ausgeben (1 Ziffer / mSek)
Sowas läßt sich leicht durchschauen und man verkorkst nicht ein von vornherein schon verkorkstes Programm. Denk dran, du willst auch nach einem Jahr noch wissen, was du da gemacht hast und da helfen dir die mageren Kommentare überhaupt nicht. Vielleicht solltest du dir doch mal meinen Rat antun und den Beitrag "keine Angst vor Assembler" in der Rubrik "FAQ" durchlesen.
Gruß old max
 
Hallo Oldmax,

das wird schon wachsen. :)

Die Ausgabe zum Display funktioniert ja inzwischen, vielleicht korrigiert er ja noch die beiden subi.

Der komplette Programmcode, außer der Initialisierung ist im Moment in der Timer ISR, das ist nicht so gut. Auch bei push und pop habe ich noch "Bauchschmerzen". Die Variablen sollte man besser anlegen und die sjmp und rcall sind manchmal unübersichtlich.

Aber wie gesagt, das wird schon ... :D


Gruß,
Dirk
 
Hallo

@ Oldmax
Ja, ich weis dass das ein ziemliches Chaos ist. aber mein vorrangiges Zihl war erstmal das Ding zum laufen zu bringen. (egal wie) ^^ jetzt bin ich mutiviert und mach mich ans aufräumen. Bei der Anortnung der verschidenen Initialisierungen usw hab ich immer das Problem dass wen ich alles so anordne dass es sauber gegliedert ist, dann funktionierts nichtmehr. -> Gestern hab ich eine Tunde gesucht und gebastelt bis mir eingefallen ist dass ich das Display garnicht einschalten kann wen ich die Ausgänge am Mega8 nicht konfiguriert habe. :D

Den Ratschlag mit "Sekunde Einer, Sekunde Zehner, Minute Einer" etc. werde ich warscheinlich umsetzen. Aber im moment bin ich froh dass ich es anders gemacht habe, sonst hät ich das mit dem ACSII nie kapiert und es is auch ne schöne übung mit der rechnerei :)

@ Dirk
Hab das mit subi-10, subi-48 gerade ausprobiert. funktioniert wie erwartet wunderbar :D

Ich mach mich jetzt mal ans Werk das ganze mal übersichtlich auf zu baun.
Gruß
 
Fertig!

So, es ist nun vollbracht, die Uhr ist endlich fertig. Vielen Dank an alle die mir bei diesem (für nen Anfänger anspruchsvollem) Projekt geholfen haben :)

Hier der fertige Code und ein Bildchen das zeigt wie die Uhr mit Hardware funktioniert xD

Code:
;************************************************
; Chip: ATmega8					;
; BD0 = PORTC,0					;
; BD1 = PORTC,1					;
; BD2 = PORTC,2					;
; BD3 = PORTC,3					;
; RS  = PORTD,4					;
; RW  = GND						;
; E   = PORTD,5					;
; Quarz = Uhrenquarz			;
;************************************************


.include "m8def.inc"
 
;Alias Namen erstellen
.def temp1 = r16
.def temp2 = r17
.def temp3 = r18
.def temp4 = r19

.def Sekunde = r20
.def Minute  = r21
.def Stunde  = r22


.org	0
	rjmp	Programmstart



.org	OVF0addr
	rjmp	Timer0_overflow

Programmstart:

;Ausgänge definieren
	LDI		temp1,0b11111111
	Out		DDRD,temp1
	LDI		temp1,0b11111111
	out		DDRC,temp1
	LDI		temp1,0b11111111
	out		DDRB,temp1

;Stack Initialisieren 
	ldi temp1, LOW(RAMEND)  
	out SPL, temp1
	ldi temp1, HIGH(RAMEND)  
	out SPH, temp1

;Zeit laden
	LDI	Sekunde,0
	LDI	Minute,27
	LDI	Stunde,12

;Untere Register laden
	LDI	temp1,60
	mov	r2,temp1
	LDI	temp1,24
	mov	r3,temp1

;Display Initialisieren
	rcall	Display_ini

;Timer Initialisieren
	LDI		temp1,  (1<<cs01) | (1<<cs00)	;Teiler=1024
	out		TCCR0,temp1
	LDI		temp1,(1<<TOIE0)
	out		TIMSK,temp1
	sei

rcall	Loop


;Uhr
;*************************************

Timer0_overflow:
	INC		temp4				;Lade 1 in temp4
	reti						;Interrupt ende

Uhr:
	clr		temp4				;Temp4 löschen
	INC		Sekunde
	CP		Sekunde,r2
	BRLO	Menue

Minute:
	CLR		Sekunde
	INC		Minute
	CP		Minute,r2
	BRLO	Menue

Stunde:
	CLR		Minute
	INC		Stunde
	CP		Stunde,r3
	BRLO	Menue

Neuer_Tag:
	CLR		Sekunde
	CLR		Minute
	CLR		Stunde
	rjmp	Menue

;Hauptprogramm
;***************************************

Menue:
rcall	LCD_löschen
	
;Stunden schreiben:
;*******************
	
	rcall	waitms5
	LDI		temp2,' '
	rcall	Print
	LDI		temp2,' '
	rcall	Print
	LDI		temp2,' '
	rcall	Print
	LDI		temp2,' '
	rcall	Print
	mov		temp1,Stunde
	clr		temp2
	ldi		temp2,'0'-1

LCD_Nummer1:
	inc		temp2
	subi	temp1,10
	brcc	LCD_Nummer1
	push	temp1
	rcall	Print
	
LCD_Nummer2:
	clr 	temp2
	pop		temp2
	subi	temp2,-58
	rcall	Print
	LDI		temp2,':'
	rcall	Print		

;Minuten schreiben
;******************
	
	mov		temp1,Minute	
	clr		temp2
	ldi		temp2,'0'-1

LCD_Nummer3:
	inc		temp2
	subi	temp1,10
	brcc	LCD_Nummer3	
	push	temp1	
	rcall	Print
	
LCD_Nummer4:
	clr 	temp2
	pop		temp2
	subi	temp2,-58
	rcall	Print
	LDI		temp2,':'		
	rcall	Print

;Sekunden schreiben
;*******************

	mov		temp1,Sekunde	
	clr		temp2
	ldi		temp2,'0'-1

LCD_Nummer5:
	inc		temp2
	subi	temp1,10
	brcc	LCD_Nummer5 
	push	temp1		
	rcall	Print
	
LCD_Nummer6:
	clr 	temp2
	pop		temp2
	subi	temp2,-58
	rcall	Print		



Loop:
	CPI		temp4,2				;Vergleiche temp4 mit 1
	BRLO	Loop				;Springe zu Loop wen nicht 1
	rjmp	Uhr					;Springe zu "Uhr"


; Display
;***********************************

Display_ini:
	rcall	Waitms20
	ldi		temp3,3
Widerholung:
	LDI		temp2,0b00000011
	Out		portd,temp2
	rcall	send
	dec		temp3
	brne	Widerholung
	LDI		temp2,0b00000010
	OUT		Portd,temp2
	rcall	send
	LDI		temp2,0b00101000
	rcall	Send_Befehl
	LDI		temp2,0b00001100
	rcall	Send_Befehl
	LDI		temp2,0b00000100
	rcall	Send_Befehl
	ret

LCD_Löschen:
	ldi		temp2,0b00000001
	rcall	Send_Befehl
	ret

Print:
	Mov		temp3,temp2
	swap	temp2
	andi	temp2,0b00001111
	sbr		temp2,0b00010000
	out		PortD,temp2
	rcall	send
	andi	temp3,0b00001111
	sbr		temp3,0b00010000
	OUT		Portd,temp3
	rcall	Send
	ret

Send_Befehl:
	Mov		temp3,temp2
	swap	temp2
	andi	temp2,0b00001111
	out		PortD,temp2
	rcall	send
	andi	temp3,0b00001111
	OUT		Portd,temp3
	rcall	Send
	ret	

Send:
	SBI		portd,5
	nop
	CBI		portd,5
	rcall	waitms5
	ret


Waitms5:
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	ret ;Rücksprung
	


Waitms20:
	LDI		temp1,10
Waitms20_1:
	dec		temp1,
	brne	waitms20_1
	ret


Uhr.jpg
 
Hier der fertige Code und ein Bildchen das zeigt wie die Uhr mit Hardware funktioniert xD

Super!

Wenn du das Programm mit weiteren Funktionen erweitern solltest, wäre es gut, wenn du das Programm erst noch einmal etwas überarbeitest. Du hast es dann nämlich erstens einfacher weitere Features hinzuzufügen und zweitens kannst du dann einfacher Programmteile für andere Projekte verwenden, wenn diese modular aufgebaut sind. Oldmax hat hat ja schon einiges dazu geschrieben.

Viel Spaß noch mit deiner Uhr :)

Gruß,
Dirk
 
Hi
Hallo Oldmax,

das wird schon wachsen.
Na, dein Wort in Gottes Ohr, denn bei irdischer Hilfe bin ich mir da nicht so sicher. Immerhin hat er viel Hilfestellung bekommen und dann das.... :fie:
Code:
;Timer Initialisieren
	LDI		temp1,  (1<<cs01) | (1<<cs00)	;Teiler=1024
	out		TCCR0,temp1
	LDI		temp1,(1<<TOIE0)
	out		TIMSK,temp1
	sei

rcall	Loop


;Uhr
;*************************************

Timer0_overflow:
	INC		temp4				;Lade 1 in temp4
	reti						;Interrupt ende
Nun mein lieber Folienkondensator....
Wieso RCALL Loop ???? Warum noch 5 mal herumhüpfen. :hmpf: Oder wirst du nach Programmzeilen bewertet ? :rolleyes:
Trotzdem wäre es allemal höchstens ein RJMP.
Außerdem, ich bin nicht so freundlich, wie Dirk, ich sag dir glatt, deine Uhr ist keine Uhr, egal was sie anzeigt. Sie kann nicht genau gehen. Also funktioniert dein Programm nicht. Es funktioniert irgendwas, aber nicht eine Uhr. Wie es im Leben halt so ist, halbfertig ist nicht fertig.:yes2:
Ich bin halt von der Sorte: Hör auf Ratschläge und versuch diese nachzuvollziehen und nicht so umzuändern, das sie in deinen Code passen. Pass deinen Code an die Ratschläge an, sonst verläufst du dich im Nirwana. Assembler kann sehr viel Spass machen und so schwer lesbar, wie manche behaupten, ist er nicht. Aber das was du da verknotet hast, ist ein Musterbeispiel mit tausend Agrumenten gegen Assembler.
Warum geizt du mit dem Timer. Du nimmst Timer 0. Warum. Damit verschwendest du sehr viel Resourcen, wenn du daraus einen vernünftigen und richtigen Zeittakt hinbekommen willst. Nimm Timer 1, der kostet auch nicht mehr, bietet dir aber für (fast) jede Taktfrequenz einen sauberen Interrupt im mSek.-Takt.
Bei 1 Mhz nimmst du keinen Vorteiler und setzt den Vergleichswert auf 1000. Bei 8 MHz nimmst du einen Vorteiler von 8 und wenn du doch 16 MHz taktest, nimmst du den Vorteiler 8 und setzt den Vergleichswert auf 2000. Nun hast du eine Timer-ISR, die jede mSek. aufgerufen wird. Brauchst du irgendwelche Verzögerungen, z.B. für Entprellroutinen, dann setz dir in der Timer-ISR Bits, die du in deiner Programmschleife abfragst, bearbeites und zurücksetzt. Du mußt dir wirklich mal Gedanken machen, wie so ein Programm arbeitet. Das wüste gepushe und gepoppe ist echt verwirrend. Nimm einen Bleistift und Papier, und mal erst mal Module drauf. z.Eingabe, Ausgabe, irgendwelche Bearbeitungsschritte, dnn sortier dir das in eine logische Reihenfolge und dann programmier die Module. Wenn du eine Arbeit abgeben sollst, brauchst du auch erst Information. Dann kannst due die Bearbeitung starten und zum Schluß erst, kannst du die Arbeit abliefern. Ist immer eine schöne regel und eine einfache Leitlinie. Der Name "EVA" stammt aber nicht von mir.(Eingabe, verarbeiten, Ausgabe)
Ach ja, nur Mut, auch wenn ich mächtig gelästert hab, es wird schon......
Gruß oldmax
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)