Assembler Uhr mit Sekundenanzeige

Folienkondensator

Neues Mitglied
28. Mai 2012
89
0
0
Sprachen
  1. ANSI C
  2. Assembler
Hallo

Und schon wider gibz ein Problem bei dem ich nicht weiter komme.
Ich würd mir gerne eine Uhr basteln. Den programmablauf hab ich mir schon ausgedacht. nur hab ich jetzt das Problem dass das Register 16 jede Sekunde um eins herhöht werden soll. nach der Zahl 9 solls wider bei null anfangen. also praktisch so 0,1,2,3,4,5,6,7,8,9,->0,1,2,3,4,5....... (im Sekunden Tackt)
Ich hab schon gelesen dass das mit irgendwelchen Interrupts möglich ist. aber wie macht man sowas?

Der bisherige Ablauf soll dann mal so aussehen (hab ich mir vorgestellt)
Ist das Sinnvoll, bzw, würde es überhaupt funktionieren?

Gruß

Code:
.include"m8def.inc"

;Es wird ein 4,0 Mhz Quarz benutzt

rjmp	Start

Start:
	ldi	r16,0b11111111		;Ansteuerung der 7Segment Ziffern
	out	ddrd,r16
	
	ldi	r16,0b00000111
	out	ddrb,r16		;Wird zum umschalten von GND der Displays benötigt
	clr	r16


;Sekundenstelle einer--------------------------------------------------------------------------------------------


Null:
	cpi	r16,0			;Vergleiche R16
	brne	Eins			;Springe nach Eins wenn r16 Nicht gleich 0
	ldi	r17,0b11111100		;wenn R16 = 0 dann Ziffer0 in r17Laden
Eins:
	cpi	r16,1			;Vergleiche r16
	brne	Zwei			Springe nach Zwei wenn R16 Nicht gleich 1
	ldi	r17,0b01100000		;wenn R16 = 1 dann Ziffer1 in r17Laden
Zwei:
	cpi	r16,2
	brne	Drei
	ldi	r17,0b11011010		;wenn R16 = 2 dann Ziffer2 in r17Laden
Drei:
	cpi	r16,3
	brne	Vier
	ldi	r17,0b11110010
Vier:
	cpi	r16,4
	brne	Fünf
	ldi	r17,0b01100110
Fünf:
	cpi	r16,5
	brne	Sechs
	ldi	r17,0b10110110
Sechs:
	cpi	r16,6
	brne	Sieben
	ldi	r17,0b10111110
Sieben:
	cpi	r16,7
	brne	Acht
	ldi	r17,0b11100000
Acht:
	cpi	r16,8
	brne	Neun
	ldi	r17,ob11111110
Neun:
	cpi	r16,9
	brne	Out			;Springe nach Out wenn R16 nicht gleich 9
	ldi	r17,ob11110110
	inc	r18			;Wenn die neunte stelle erreicht/Überschritten wird,
Out:					;wird das register 18 um eins erhöht.
	out	portd,r17		;Portd gibt den Wert r17 aus
	clr	r17			;r17 wird gelöscht



;Sekundenstelle Zehner-----------------------------------------------------------------------------------------


Null1:
	cpi	r18,0
	brne	Eins1
	ldi	r17,0b11111100
Eins1:
	cpi	r18,1
	brne	Zwei1
	ldi	r17,0b01100000
Zwei1:
	cpi	r18,2
	brne	Drei1
	ldi	r17,0b11011010
Drei1:
	cpi	r18,3
	brne	Vier1
	ldi	r17,0b11110010
Vier1:
	cpi	r18,4
	brne	Fünf1
	ldi	r17,0b01100110
Fünf1:
	cpi	r18,5
	brne	Out1
	ldi	r17,0b10110110
	inc	r19
Out1:
	out	portd,r17
	clr	r17

;Minutenstelle einer----------------------------------------------------------------------------------------


Null2:
	cpi	r19,0
	brne	Eins2
	ldi	r17,0b11111100
Eins2:
	cpi	r19,1
	brne	Zwei2
	ldi	r17,0b01100000
Zwei2:
	cpi	r19,2
	brne	Drei2
	ldi	r17,0b11011010
Drei2:
	cpi	r19,3
	brne	Vier2
	ldi	r17,0b11110010
Vier2:
	cpi	r19,4
	brne	Fünf2
	ldi	r17,0b01100110
Fünf2:
	cpi	r19,5
	brne	Sechs2
	ldi	r17,0b10110110
Sechs2:
	cpi	r19,6
	brne	Sieben2
	ldi	r17,0b10111110
Sieben2:
	cpi	r19,7
	brne	Acht2
	ldi	r17,0b11100000
Acht2:
	cpi	r19,8
	brne	Neun2
	ldi	r17,ob11111110
Neun2:
	cpi	r19,9
	brne	Out2
	ldi	r17,ob11110110
Out2:	
	out	portd,r17
	clr	r17
	rjmp	Null
	
;Fortsetzung folgt!
 
Hallo,

manometer hast du das kompliziert gemacht. Aber naja ... wenns läuft.

Das mit dem Muster in r17 würde ich über eine Subroutine und nen Pointer auf eine Datentabelle machen. Das geht wesentlich eleganter ;)

Das mit dem Interrupt und dem Timer ist im Datenblatt immer als Beispiel drin. Da hast du für jeden Bereich kleine Beispiele in C und Assembler drin. Beim Mega8 stimmen die sogar mit den Registernamen überein. bei größeren Controllern passen die Registernamen nicht mehr so ganz ;)

Gruß
Dino
 
Gehts auch einfacher? Je einfacher desto besser xD
Aber leider firndet man auch mit Google keine verständlichen Beispiele wie man ne Uhr bastelt :/
Gruß
 
Hallo,

Gehts auch einfacher? Je einfacher desto besser xD
Aber leider firndet man auch mit Google keine verständlichen Beispiele wie man ne Uhr bastelt :/
du kannst statt des Timers auch mit Zeitschleifen arbeiten. Also in mehreren ineinanderliegenden Schleifen Zeit "verbraten".
Das wird allerdings recht ungenau und der Controller macht in der Zeit in der er in den Schleifen rennt nix anderes. Also auch keine Tastenabfrage für "Uhr stellen" oder so.

Über kurz oder lang wirst du nicht an Interrupts vorbeikommen. Je eher du dich damit auseinandersetzt desto schneller wirst du die Vorzüge schätzen lernen die du damit erhälst.

Im Endeffekt zählt der Timer die Takte des Systemquarzes bis zu einem eingestellten Zählerstand. Dann erzeugt er ein Ausnahmesignal (einen Interrupt) und setzt (wenn du es so einstellst) den Zähler für die nächste Runde wieder auf Anfang.
Durch den Interrupt wird ein Unterprogramm (Interrupt Serviceroutine oder ISR) angesprungen. Dieser Aufruf der ISR wird über den Sprungbefehl ausgeführt, der an der entsprechenden Stelle für diesen Timerinterrupt in der Interruptvektortabelle am Anfang des Flash steht.

Also in der Kürze ...
- Durch einen bestimmte Aktion wird ein Interrupt erzeugt.
- Je nach Typ des Interrupts (ADC, Timer, ...) wird der Befehl aufgerufen der an der entsprechenden Stelle der Interruptvektortabelle steht und die Interrupts global gesperrt.
- Der Befehl in der Vektortabelle ist normalerweise ein Sprung zur ISR die dann diesen Interrupt behandelt. Also das ausführt was man sich dafür als Aktion ausgedacht hat.
- Am Ende der ISR steht kein RET sondern ein RETI !! Also Return From Interrupt. Damit wird das Programm dort weitergeführt wo es der Interrupt rausgerissen hat und die Interrupts werden wieder global freigegeben.

Gruß
Dino
 
Hi
Dann will ich dir mal ein klein wenig unter die Arme greifen. Hier mal einen Auszug meiner "Rentneruhr". Am Anfang steht die IVT (Interrupt Vector Table) eines Atmega16. Der Atmega8 ist aber fast gleich aufgebaut. Daher ist diese Info auch so zu übernehmen.
Code:
;************************   Programm   Interruptvektoren ************************
;* Die Interruptvectoren sind in einer Art Tabelle fest im Controller angelegt. *
;* Tritt ein Interrupt auf, so löst er einen Sprung an die entsprechende Stelle *
;* in der Tabelle aus. Sprung ist eigentlich nicht richtig, er lädt in das 	*
;* Adressregister die zugehörige Adresse und holt von dort den nächsten Befehl.	*
;* Diesen Vorgang nennt man allgemein "Sprung". Außerdem ist dieser Sprung	*
;* aufgebaut wie ein Unterprogrammaufruf mit Ablegen der Rücksprungadresse  auf *
;* dem Stack. Ist keine Bearbeitung eines Interruptes vorgesehen, steht dort 	*
;* nur RETI.  (Return from Interrupt.)						* 
;* Und das ist auch gleich meine Empfehlung. So oft wie möglich nicht  die 	*
;* Kürzel lesen	sondern sofort in ausgesprochene Worte fassen. Dann verliert 	*
;* Assembler ganz schnell den Ruf der Unlesbarkeit. Es dauert dann nicht lang, 	*
;* dann sind 50-70% der Sprache erlernt und man sieht immer seltener in die 	*
;* Assemblerhilfe oder sucht  einen Befehl. Ein	Interrupt wird dadurch 		*
;* bearbeitet, das man ein Unterprogramm schreibt, dieses aber nicht mit Call	*
;* aufruft, sondern mit JMP. Es ist einfach nur ein  "Weiterleiten" in eine 	*
;* Befehlsfolge.								*
;********************************************************************************
.ORG INT_VECTORS_SIZE 

.org INT0addr 	RETI			; External Interrupt0 Vector Address    
.org INT1addr	RETI			; External Interrupt1 Vector Address       
.org OC2addr	RETI			; Output Compare2 Interrupt Vector Address       
.org OVF2addr	RETI			; Overflow2 Interrupt Vector Address       
.org ICP1addr	RETI			; Input Capture1 Interrupt Vector Address       

.org OC1Aaddr	RJMP 	isrTimer1   	; Einsprungadresse ISR Timer0

.org OC1Baddr	RETI  			; Output Compare1B Interrupt Vector Address       
.org OVF1addr	RETI  			; Overflow1 Interrupt Vector Address       
.org OVF0addr	RETI  			; Overflow0 Interrupt Vector Address       
.org SPIaddr	RETI  			; SPI Interrupt Vector Address       
			              
.org URXCaddr	RJMP	int_rxc		; USART Receive Complete Interrupt Vector Address       
			 
.org UDREaddr	RETI  			; USART Data Register Empty Interrupt Vector Address       
.org UTXCaddr	RETI  			; USART Transmit Complete Interrupt Vector Address       
.org ADCCaddr	RETI  			; ADC Interrupt Vector Address       
.org ERDYaddr	RETI  			; EEPROM Interrupt Vector Address       
.org ACIaddr	RETI  			; Analog Comparator Interrupt Vector Address       
.org TWIaddr	RETI  			; Irq. vector address for Two-Wire Interface       
.org SPMRaddr	RETI  			; SPM complete Interrupt Vector Address       
			     
;********************************************************************************
;* Das Programm startet mit dem Initialisierungsabschnitt. Ganz vorn ist die 	*
;* Zuweisung für den Stack. Darauf folgen die Initialisierungen für die Timer, 	*
;* den USART, die Vorbesetzung der Register und der Variablen. Dabei werden 	*
;* auch die Parameter aus dem EEProm in den Speicher kopiert. Danach wird die	*
;* Schaltung freigegeben und ein Testruf angestoßen. Nach einer kurzen Pause 	*
;* erfolgt der Einstieg in die Programmschleife.				*
;******************************************************************************** 
  
;----------------- Hauptprogramm Initialisierung und Schleife -------------------

Start:	
	LDI	Reg_A ,high(RAMEND)	; Stack Pointer auf  RAMEND
	OUT	SPH, Reg_A		; definiert in m8def.inc (s.o.) 
	LDI	Reg_A, low(RAMEND)	; 
	OUT	SPL, Reg_A  

	RCALL	Init_Timer1		; Timer 1 initialisieren 
	;RCALL	Init_Uart			; USART initialisieren 
	RCALL	Init_IO			; Ein-Ausgaben initialisieren 
	RCALL	Init_Register		; Variable besetzen 
	RCALL	Init_Monate
	RCALL	Init_Matrix_Zahlen
	RCALL 	Init_Matrix_Zeichen
	RCALL 	Init_Matrix_Muell
	SEI		

;-------------------------- Begin Hauptprogramm ---------------------------------
;****************************************************************
;* Das Hauptprogramm startet ist eine Endlosschleife, in der die Anweisungen	*
;* ständig wiederholt werden. 						*
;**************************************************************** 
Loop:		
	RCALL	Read_IO			; Eingänge einlesen
	RCALL	IO_Debounce		; Prellzeit
	LDS	Reg_A, Time_Flag               ; diese Bits werden in der Timer-ISR gesetzt   
	ANDI	Reg_A, 0b00010000
	BREQ	Chk_Seconds
	
	LDS	Reg_A, Time_Flag
	ANDI	Reg_A, 0b11101111            ; Zeitereignis quittieren
	STS	Time_Flag, Reg_A
	RCALL	Is_Time_Cnt                      ; hier wird die Zeit gezählt
	
Chk_Seconds:
	LDS	Reg_A, Time_Flag
	ANDI	Reg_A, 0b00000100
	BREQ	Chk_Events
	
	LDS	Reg_A, Time_Flag
	ANDI	Reg_A, 0b11111011          ; dieses Bit signalisiert einen 1/10 Sekunden Impuls
	STS	Time_Flag, Reg_A
	RCALL	Is_Cnt_Dwn
	
Chk_Events:
	
	RCALL	Chk_Event_1		; Taster 1- Umschaltung 4 Betriebsarten 
	RCALL	Chk_Event_2		; Taster 2- Weiterschalten Ziffern 
	RCALL	Chk_Mode_0		; Count Down Programm
	RCALL	Chk_Mode_1		; Anzeige Uhr Programm
	RCALL	Chk_Mode_2		; Setze Uhrzeit
	RCALL	Chk_Mode_3		; Setze Zielzeit
	RCALL	Set_Matrix
	;RCALL Calc_Counter
	;RCALL	Read_Com		; PC-Kommunikation lesen
	;RCALL	Set_Out			; LED- und Relaisausgabe
	;RCALL	Send_PC			; PC-Kommunikation schreiben
RJMP	Loop
Ich hab nicht alles rausgestrichen, damit du einmal ein Gefühl bekommst, wie ein Programm aufgebaut werden sollte. Grad bei Assembler ist es wichtig, die Codeabschnitte nicht nur zu dokumentieren, sondern auch in gut lesbare Blöcke aufzuteilen. Die Variable "Time_Flag" ist ein Signalbyte, in das die Timer-ISR Bits setzt. Im Programm werden diese Bits dann ausgewertet und entsprechend bearbeitet.
Es ist nicht wichtig, das die Bearbeitung ein paar mSek. später erfolgt, denn das Zeitraster passt. Du verlierst da keine Zeit. Hier mal die Timer Initialisierung
Code:
;-------------------------------Timer 1 Parametrierung ----------------------------
;*****************************************************************
;* Timer 1 ist die Zeitbasis für alle zeitabhängigen Ereignisse. Er erzeugt einen             *
;* Interrupt je mSek In den Zeitregistern weden diese mSek. aufaddiert und die 	  *
;* Zeitbasis gebildet. Ist vielleicht mal für eine andere Anwendung von Interesse.         *
;* Aber die Zeitbasis ist abhängig vom Takt. In diesem Fall muß die Taktfrequenz          *
;* 8 MHz sein.								  *
;*****************************************************************
Init_Timer1:	
	LDI	Reg_A, high( 1000 - 1 )			; Set Compare Value for one msek
	OUT	OCR1AH, Reg_A        
	LDI	Reg_A, low( 1000 - 1 )        
	OUT	OCR1AL, Reg_A    
	

	; CTC Modus einschalten                                   
	; Vorteiler auf 1        
	LDI	Reg_A, ( 1 << WGM12 ) | ( 1 << CS10 )        
	OUT	TCCR1B, Reg_A         
	LDI	Reg_A, 1 << OCIE1A	
	; OCIE1A: Interrupt bei Timer Compare        
	OUT	TIMSK, Reg_A
RET
;***********************************************************************
und noch und die Timer ISR
Code:
;***********************************************************************
;-- -----------------------Interruptroutine Timer 1 für Zeitbasis -----------------------
;***********************************************************************
;* Die Aufgabe dieser Routine ist eine Zeitbasis für das Programm zu liefern. Alle 		*
;* hier und in den von hier aufgerufenen Unterprogrammen benutzten Registerinhalte 		*
;* müssen gesichert werden. Die kleinste Zeiteinheit sind mS. Diese werden zu Sekunden 	*
;* geteilt. Größere Zeiteinheiten werden im Programm daraus mit einem Flag abgeleitet.	*
;* Damit sind alle Zeitgesteuerten Aktionen abgedeckt.       				*		
;***********************************************************************
 
isrTimer1:	

	PUSH	Reg_A			             ; Register sichern
	IN		Reg_A, SREG		; SREG sichern    
	PUSH	Reg_A
	

;******************************* 1 ms ***********************************		
	RCALL	Segment_Out			; Aufruf Multiplexer Anzeige
	LDS		Reg_A, Time_Flag
	ORI		Reg_A, 0b00000011	; Flags für Millisekunden
	STS		Time_Flag, Reg_A
	INC		mSek
	CP		mSek, Hundert
	BRLO	End_Isr1
	CLR		mSek				
	LDS		Reg_A, Time_Flag
	ORI		Reg_A, 0b00001100	; Flag für Zehntelsekunde
	STS		Time_Flag, Reg_A
	
	INC		Zehntel
	CP		Zehntel,Zehn
	BRLO	End_Isr1
	CLR		Zehntel
	LDS		Reg_A, Time_Flag
	ORI		Reg_A, 0b00010000	; Flag für Sekunde
	STS		Time_Flag, Reg_A
	

End_Isr1:	

	
	POP		Reg_A    
	OUT		SREG, Reg_A		; SREG wiederherstellen
	POP		Reg_A
		
RETI
der Aufruf für den Multiplexer der Anzeige ist hier in die ISR eingebettet. Dadurch wird eine gleichmäßige Helligkeit erreicht und die Anzeige "flimmert" nicht.
Aber ein bischen sollst du ja auch noch machen. Schau mal in die FAQ. Da hab ich mal im Beitrag "Keine Angst vor Assembler" ein paar Informationen abgelegt.:wink:
PS:
Da die Formatierung etwas misslungen ist, kopier einfach den Inhalt in eine Textverarbeitung oder gleich in das AVR Studio. Dann ist es besser lesbar. ich hab leider keine Zeit mehr, da noch Hand anzulegen..
Gruß oldmax
 
Nunja, Also nach langem lesen und googlen hab ich den Text um gottes Willen immer noch nicht ansatzweise verstanden. ich denke es wär doch besser wenn ich mir mal ein etwas leichteres Projekt suche. ^^
Bei meiner Googlerei bin ich auf hunderte Basic Programme gestoßen. und mir ist aufgefallen dass die im Vergleich zu Assembler grade mal 1/10 so lang sind. und relativ "logisch" Ist das normal oder war das Zufall?
Ich bin zb. auf ein Programm gestoßen das den Analog Eingang des ATmega8 abfrägt und je nachdem wie groß die EIngangsspannung ist leuchtet LED1, LED2 oder LED3 auf.
Ein sehr sehr einfachs Programm finde ich (in Basic) und jetzt nur mal interessehalber die Frage. Wie würde genau dieses Programm in Assembler aussehen? oder was müsste man tun um die Aufgabe über Assembler genau so zu lösen?

Hier mal der Code.
Wen jemand mal Lust und Zeit hat würde ich mich freuen wenn man das mal übersetzn könnte (wenn möglich natürlich mit viel viel erklärungstext hinter jedem Befehl) ^^
MfG

$regfile "m8def.dat"

$crystal = 4000000
Dim Ad As Word
Config Portb = Output
Config Adc = Single , Prescaler = Auto , Reference = Avcc

Start ADC

Do

Ad = Getadc(0)

If Ad <= 300 Then
Portb , 0 = 1 : Portb , 1 = 0 : Portb , 2 = 0

Elseif Ad > 300 and Ad < = 450 Then
Portb,0 = 0 : Portb,1 = 1 : Portb,2 = 0

Elseif Ad > 450 Then
Portb,0 = 0 : Portb,1 = 0 : Portb,2 = 1



End If
Loop
 
Hi
Nee, ganz soviel Freizeit will ich da denn nun doch nicht opfern, blos weil dir Assembler "zu schwer" ist. Ich hab dir ein paar Tipps gegeben. Wenn du Bascom einfacher findest, dann programmier in Bascom. In Assembler müssen alle Initialisierungen noch von Hand vorgenommen werden. Bascom nimmt dir da viel ab. Aber, diese Initialisierungen machst du einmal, später nimmst du diese Blöcke und baust neue Programme drum herum. Dann ist Assembler auch nicht mehr so riesig. Außerdem, wenn ich aus dem Code, den ich da veröffentlicht habe, unnütze Calls herauslöse, und nur den eigentlichen Kern reinschreibe, ist er gar nicht mehr soooo groß. Dann erkennst du aber keinerlei Zusammenhänge. Also, mach dich dran und lern etwas mit Assembler umzugehen oder lass es. Gelegentlich wirst du vielleicht etwas finden, was in einer höheren Sprache einfacher ist. Denk dann mal drüber nach, ob und wie es in Assembler aussehen könnte, aber verschieb diesen Job nicht auf andere.
Schönen Tag noch
oldmax
 
Naja, Asembler ist mir auf jeden Fall lieber. deswegen frag ich ja ^^
Mein Problem ist nur dass ich die Zusammenhänge von irgend welchen "inizialisierungen" nicht verstehe. Gibz hier im Forum nicht so ne Art Beispielsammlung in der ganz ganz einfache Programme bzw einfache Vorgänge dargestellt sind?
Ich hab zb. riesen Probleme mit dem rcall Befehl gehabt bis ich nach stundenlanger Suche heraus bekommen habe dass man da auch erstmal etwas initialisieren muss
ldi r16, HIGH(RAMEND) ; HIGH-Byte der obersten RAM-Adresse
out SPH, r16
ldi r16, LOW(RAMEND) ; LOW-Byte der obersten RAM-Adresse
out SPL, r16
clr r16

Und genau diese kleinen dinge sind das was mir probleme bereitet.
Für den ders kann ist das selbstverständlich. aber erklär mal einem Busch Menschen wie er Auto fahren muss. Dass man dem erst erklären muss wie man die Autotür auf sperrt wird dann schnell vergessen^^

Und bei dem Timer und den interrupts ist es bei mir das gleiche. Befehle lesen ist kein Ding. und die Erklärungen was der Befehl ist auch ok. aber Wiso man das mit dem Befehl macht. und wiso man das braucht wär auch ganz interessant. xD
@oldmax. dein Schript hat mir schon weiter geholfen. da ich jetzt mal weis was da alles dran hängt.

Gruß
 
Hi
Nun, welches meinst du ? Das was ich dir empfohlen hab, oder die Codeschnipsel ?
Lies doch mal unter der Rubrik FAQ den Beitrag "keine Angst...." Vielleicht auch mal "das kleine Wissen um den µC " oder die vielen anderen Beiträge zum Thema µC.
Da steckt sehr viel Zeit und Energie dahinter. So ist z.B. bei Mikrocontroller.Net auch ein Tutorial für Assembler. Sehr lesenswert.
Das Problem, nicht zu wissen, was so ein Ding macht, wenn man es will und was dazugehört, um es entsprechend zu erziehen.. das Problem hab ich auch schon erkannt. Nur, glaub mir, das wird ein Buch mit einigen hundert Seiten und dann ist da immer noch nicht alles drin. Ich arbeite schon über ein Jahr daran und hoffe es dieses Jahr im Herbst fertig zu haben. Das, was du da angesprochen hast, ist die Initialisierung des Stack. Es gibt Controller, die machen das von Haus aus, andere nicht. Ich hab mir auch angewöhnt, den Stack immer zu initialisieren, was solls. Klar, bei einem Call muss der Controller ja wissen, wo er seinen Seitensprung getätigt hat. Das ist so, als würde man sich noch einmal das Gesicht der eigenen Frau einprägen...:D

Naja, Asembler ist mir auf jeden Fall lieber. deswegen frag ich ja
Nun erklär doch mal, warum Assembler dir lieber ist. Es gibt zwar Gründe, aber die sollst du mir selber nennen. Es ist nämlich gar nicht so einfach, das zu begründen. Wenn man diese Einstellung einer ernsten Prüfung unterzieht, verlieren die Begründungen sehr schnell an Bedeutung. Und mit dieser Meinung an der richtigen Adresse und die nehmen dich auseinander wie ein dreijähriger sein Spielzeug. Danach willst du nur noch ein Bier....:confused:
Also lies erst mal und wenn du dann gezielte Fragen hast, dann kann man auch eine Antwort geben.
Gruß oldmax
 
Hallo,

Bei meiner Googlerei bin ich auf hunderte Basic Programme gestoßen. und mir ist aufgefallen dass die im Vergleich zu Assembler grade mal 1/10 so lang sind. und relativ "logisch" Ist das normal oder war das Zufall?
laß dich nicht von der Länge des Quellcodes blenden.
Der Quellcode beim Basic ist wesentlich kürzer weil sich hinter jedem Befehl mehrere zig Asssembler-Befehle verbergen. Das blättert sich also in Assembler lediglich auf. Spätestens wenn du das Programm compiliert bzw assembliert hast wirst du sehen das das Ergebnis das in den Controller geschoben wird bei Assembler ne Ecke kürzer und schneller ist.

Gruß
Dino
 
Eins noch wegen RCALL und der Stackpointer-Initialisierung:
Was macht RCALL? Schauen wir mal ins AVR Instruction Set auf Seite 114:
Relative call to an address within PC - 2K + 1 and PC + 2K (words). The return address (the instruction after the RCALL) is stored onto the Stack. See also CALL. For AVR microcontrollers with Program memory not exceeding 4K words (8K bytes) this instruction can address the entire memory from every address location. The Stack Pointer uses a post-decrement scheme during RCALL
Ok, die Rücksprungadresse wird gepusht, dh sie wird im SRAM an der Stelle abgelegt, wo der Stackpointer grad hinzeigt (und hinterher wird dieser dekrementiert). Das ganze natürlich mit beiden Adressbytes.
Wenn der Stackpointer jetzt also auf irgendwelche anderweitig genutzen Bereiche zeigt ("Daten", oder insbesondere die I/O-Register), kommts zu "komischen Effekten".
Wohin zeigt der SP denn so?
-Wenn man Ihn selbst irgendwohin gesetzt hat ists klar (so auch in Deinem Codeschnipsel -> RAMEND)
- wenn bytes gepusht/poppt wurden, halt die byteanzahl drunter oder drüber (auch da kann es zu Überläufen kommen, wenn man sich logisch vertut)
Und wo Zeigt der SP hin, solange man noch nichts mit ihm angestellt hat?
Das steht im Datenblatt des Controllers - bei den eher älteren Controllern meist 0x0000, bei den eher moderneren meist RAMEND.
Insofern stimmt die oft gelesene Regel "Man muß, wenn man den Stack verwenden will/muß erstmal den Stackpionter initialisieren!" schlichtweg nicht. Der Stackpointer wird immer(!) durch den Reset initialisiert - wie eben auch alle anderen I/O-Register. Ob die initialisierten Default-Werte jetzt Sinn machen (früher halt alles 0), mußt Du jeweils (Register) dem jeweiligen (Controller) Datenblatt entnehmen. Die Regel lautet eher:
"Man muß, wenn man den Stack verwenden will/muß erstmal sicherstellen, daß der Stackpointer sinnig (re)initialisiert worden ist!"
Die Controller sind da gnadenlos - die machen jeden Mist mit den Du instruierst...
 
Nun erklär doch mal, warum Assembler dir lieber ist. Es gibt zwar Gründe, aber die sollst du mir selber nennen. Es ist nämlich gar nicht so einfach, das zu begründen.

Naja, weil ich mit Assembler angefangen hab und ich zumindest schon so weit bin dass ich Lämpchen blinken lassen und Taster abfragen kann. Und mittlerweile weis ich auch schon wie man Fuse Bits setzt :D und das ist schon mal was schönes wenn man Ergebnisse anhand blinkender Lämpchen sehen kann. Auserdem hab ich kein Basic oder C Buch in dem man mal nach gucken kann :p
UND mit Assembler ist eben Alles nur erdenkliche machbar. und soweit ich mitbekommen hab kommt man auch bei C oder Basic nicht um Assembler vorbei wenns mal brennt. :)
 
Hi
Na, das ist doch mal ein Agrument.... :rolleyes:
UND mit Assembler ist eben Alles nur erdenkliche machbar.
Na dann viel Spaß, bei einer Division zweier Realwerte....:sarcastic: oder Wurzelziehen.....:aetsch:
Nur weil eine LED blinkt... ? Das geht in Bascom und in C auch ohne Probleme. Ich will es dir sagen: Weil Assembler sehr hardwarenah ist und du den Controller höchstpersönlich kennst. Als Assemblierer kennst du die Adresse, wo der Timer sitz, oder er seine Flags hat. Du weißt, was du anstellen musst, wenn du mit I²C ein kleines Netz aufbaust und welche Portbits sich am besten eignen. Du weißt, an welchen Adressen deine Variablen liegen und wie du das nutzen kannst. (siehe OpenEye). Du kennst den Wert vom Stackpointer und Adresscounter. Aber, wenn du glaubst, es ist einfach, eine komplexe Mathe aufzusetzen, dann wirst du ein paar Probleme bekommen. Ich sage nicht, das es nicht geht, aber wir sind durch das Dezimalsystem so verdorben.... dabei braucht es doch nur die Ziffern "0" und "1"....:cool:
Ich sage dir jetzt, warum <ich> Assembler nehme:
Weil ich es vor 30 jahren schon einmal gelernt hab (auf einem Z80)
Weil ich da sehr Hardwarenah bin und das auch verstehe
Weil ich es einfach finde, allerdings gibt es auch für mich Grenzen
Weil meine Programme auf einem Controller eher logisch sind als mathematisch (berufsbedingt)
und weil ich ehrlich gesagt von C keine Ahnung habe und zu faul bin, noch Bascom zu lernen
Sollte ich aber jemals eine Anwendung haben, wo ich um mathematische Prozesse nicht herum komme, werde ich mir C oder Bascom reinpfeifen. Wird zwar schon langsam schwerer, aber ist noch nicht unmöglich.
So, nun will ich die Debatte aber, welche Programmiersprache zu empfehlen ist, auf meiner Seite abbrechen. Wenn du meinst, du willst Assembler machen, dann tu es, egal was man dir sonst so empfieht. Aber versuche nie, das zu rechtfertigen und mit einer Aufzählung von Vorteilen zu dabattieren, von denen du (noch) keine Ahnung hast.
Also, zu deinem Projekt "Uhr". Welche Anzeige schwebt dir da vor ? LCD, 7-Segment oder gar Analog mit wandernden LED's
Das ist mal eine Diskussion wert.
Gruß oldmax
 
Nunja. Also die Uhr sollte Sekunden, Minuten und natürlich Stunden anzeigen. es sonn nur eine ganz einfache Uhr werden. (Kein Wecker oder solchen Krams) -> Zeit einstellen muss auch nicht unbedingt sein, da ich die Uhr ja hauptsächlich mache um mal etwas praktische Erfahrung in sachen Timer, Interrupts etc zu sammeln.

Erst habe ich an eine 7 Segment Anzeige gedacht. aber da man da ja doch ättliche AUsgänge benötigt. dann dacht ich an eine Anzeige mit leds "linien" bei der die LED von ganz unten (1) bis ganz oben (9) wandert (0)= alles aus und man sich so die Zeit bei 6 Türmen für Std;Min;Sek zusammen zählen kann. Dafür bräuchte man aber genau 30 Ausgänge mit Matrixansteuerung. Und wie das der Teufel so will hat der Mega16 nur 29!!! ^^
Also hab ich mich ganz einfach für eine Binär Uhr entschieden. (benötigt 18 Ausgänge) die Uhr ist zwar nicht ganz so einfach zu lesen, aber dafür selten xD

Bei dem Programmaufbau hab ich nur die Anfängerlösung parat. ->
r16 jede Sekunde um 1 erhöhen.
Wenn r16 60 erreicht hat soll r17 um eins erhöht und r16 gelöscht werden.
Wenn r17 60 errieicht hat soll r18 um eins erhöht und r17 gelöscht werden.
Wenn r18 24 erreicht hat soll r16,r17 und r18 gelöscht werden.

Das programm frägt die Register ab und gibt den dazugehörigen Binärcode aus. Schreibarbeit ohne Ende aber was einfacheres fällt mir nicht ein ^^ (ca 140 Portzustände).

Aufbau der LED Anzeige:

Zahlen: 1 2 4 8 16 32 | 1 2 4 8 16 32 | 1 2 4 8 16 32

LEDs--- * * * * * * | * * * * * * | * * * * * *

---------Std-----------Min------------Sek

Einfach und immer eine schöne Übung wen man auf die Uhr schaut xD
Gruß
 
Hi
Also, mal vorweg.... eine Uhr ist schon eine kleine Herausforderung für einen Anfänger, aber in meinen Augen genau das richtige, um eine Programmiersprache zu lernen. Der Atmega 16 ist auch in meiner Rentneruhr und steuert zwölf
7- Segmentanzeigen. Und ich hab immer noch Ausgänge frei.
Das Zauberwort heißt "multiplexen".
Also, nimm für die Sekunden, Minuten und Stunden Register aus den unteren Reihen... Dazu ein Register mit 60 vorbesetzt und eines mit 24
So in der Art
Code:
LDI   R16, 60
Mov  R2,R16
LDI   R16,24
Mov  R3, R16

Der Grund, die unteren Register arbeiten u.a. nicht mit dem LDI und CPI -Befehl. Das ist aber kein Problem, da ja genug Register verfügbar sind. Wenn du mehr Durchblick brauchst, dann benenne die Register einfach um.
Code:
.DEF 	sechzig			= R1 		; Zwischenspeicher aus anderen Merkern
.DEF 	vierundzwanzig		= R2 		; Anzahl der Wertebyte
.DEF	Sekunde			= R3		; Sekunden 
.DEF	Minute			= R4		; Minuten
.DEF	Stunde		                = R5		; Stunden
.DEF	Zehn			= R10		; Vergleich auf 10

Nun hast du 3 Register, die die aktuelle Zeit zählen können und 2 Register, um die Grenzen zu prüfen. Das erfolgt in der Timer-ISR.

Code:
Timer_ISR:
    Push        ....    ; benutzte Register sichern
    ....                  ; Vorteilen bis zum Sekundentakt
Sek: 
   INC      Sekunde
   CP       Sekunde, Sechzig
   BRLO   End_Timer_ISR
   CLR     Sekunde
   INC     Minute
   CP       Minute, sechzig
   BRLO   End_Timer_ISR
   CLR     Minute
   INC     Stunde
   CP       Stunde, vierundzwanzig
   BRLO   End_Timer_ISR
   CLR     Stunde
End_Timer_ISR:
   POP   ...    Alle Register in umgekehrter Reihenfolge zurückschreiben
RETI
Ok, das soll erst mal genug Code sein. Zum Thema multiplexen gibt es auch schon Beiträge und in den FAQ bestimmt eine gute Hilfe. Nun zur Bedienung. Die Uhr mußt du schon stellen, sonst macht es keinen Spaß. Und vielleicht denkst du später mal darüber nach, ob du nicht auch 12 stellig sein möchtest und vielleicht noch das Datum mit ausgibst... oder, wie ich es gemacht habe, verschiedene mit 7Segment darstellbare Worte einblendest. "PAUSE", "IcH GEH bALd", "FEIErAbENd" und noch so 'nen Kram. Ok, das geht nicht von jetzt auf gleich, aber so ein Projekt wächst mit der Zeit. Überleg mal, was du dafür brauchst und wie die Vorgehensweise sein könnte, damit du unterwegs nicht den Faden verlierst. Ich mach das bei großen Projekten immer mit Bleistift und Papier und zeichne auf, welche Module gebraucht werden. Sowas wie ein Struktogramm. Da wird dir dann schnell klar, wie du vorgehen mußt.
Ach übrigends, ich hab noch 1299 Tage, 7 Stunden 9 minuten und 29,4 Sekunden, bis ich 65 werden...... die Rentneruhr steht direkt vor mir auf dem Schreibtisch und jeder weiß zu den entsprechenden Zeiten: jetzt darf ich den Alten nicht stören, er hat "Pause".....:cool:
Gruß oldmax
 
Hi oldmax,

ich hab mir mal erlaubt bei den [avrasm]-Teilen von dir ein paar Leerzeilen einzubauen. Sie verbessern die Lesbarkeit ganz enorm. Jetzt sind die Code-Teile optisch vom beschreibenden Text getrennt. Ich hab echt Augenkrebs bei dem Bandwurmtext bekommen ;) Ich hoffe mal es ist dir recht. :eek:

Gruß
Dino
 
Hi Dino
Das kommt davon, wenn man nur mal kurz in der Frühstückspause eine Antwort schreiben will und dann sich im Forum vertut.... :pleasantry:
Jetzt ist's so, wie ich es eigentlich wollte.
Gruß oldmax
 
Hallo oldmax

Also. hab jetzt mal etwas rum geräzelt und bin zu dem Schluss gekommen dass ich ganz dringend ne Codetabelle mit ausführlicher Erklärung zu allen Assembler Befehlen brauch xD

Bis zu "timer ISR" hab ichs kapiert. aber dann gehts los.
Nen Timer habe ich noch nie benutzt und weis auch nicht wie man den einstellt. und leider gibz auch nirgends beispiele wo es ersichtlich ist welcher befehl für was sinnvoll ist.

dann dieser CP Befehl. der Vergleicht die beiden Register (subtrahiert die Zahlen) soweit hab ichs gefunden. aber der nachfolgende Befehl "BRL0", hängt der mit dem CP befehl zusammen (so wie DEC und BRNE?) und was macht der dann genau. Zu End Timer ISR springen wenn cp=0?

Ich habs mir jetzt folgendersamsen zusammen gereimt (so wärs teils Sinnvoll)

Timer_ISR:
Push .... ; benutzte Register sichern
.... ; Vorteilen bis zum Sekundentakt
Sek:
INC Sekunde ; Sekunde wird um 1 erhöht
CP Sekunde, Sechzig ; Sekunde wird mit 60 verglichen
BRLO End_Timer_ISR ; Wenn "sekunde" = 60 dann springe nach "END Timer ISR"

; Jetzt nehme ich an dass der POP Befehl die Register Sek, Min, Std wider auf 0 setzt. aber
; dann wär die Uhr doch wider auf NULL oder? Oder bearbeitet der Befehl nur Register die bei "CP"
; den gewünschten Wert erreicht haben? oder setzt der den Timer zurück?

CLR Sekunde ; Setzt Sekunden auf 0
INC Minute ; Minute um eins erhöhen. usw.
CP Minute, sechzig
BRLO End_Timer_ISR
CLR Minute
INC Stunde
CP Stunde, vierundzwanzig
BRLO End_Timer_ISR
CLR Stunde
End_Timer_ISR:
POP ... Alle Register in umgekehrter Reihenfolge zurückschreiben
RETI

Wie läuft das mit den Minuten. sollte nicht wenn die Sekunde 60 erreicht hat die Minute um eins erhöht werden. bzw ein Rücksprung nach "CP minute seckzig"
zu "inc Sekunde"?

Wär super wenn du mir da weiter helfen könntest. -> Vorallem das mit dem Timer bei dem ich immer noch nicht weis was wie man das Ding eigendlich einstellt. ^^

Ach ja, und wiso hat jeder einen Code einfügen Knopf auser ich? :rolleyes:
Gruß
 
Hi

Also. hab jetzt mal etwas rum geräzelt und bin zu dem Schluss gekommen dass ich ganz dringend ne Codetabelle mit ausführlicher Erklärung zu allen Assembler Befehlen brauch xD
Da kann ich dir die Datenblätter der Controller empfehlen. Zumindest steht im Instruction Set der Befehl mit der englichen Erklärung. Das sollte zumindest mal helfen.
Auch in AVR Studio ist unter Hilfe ein Text zu jedem Assemblerbefehl hinterlegt. Da darfst du dich ruhig mal hinbewegen, denn für einen Privatlehrer wirst du hier niemanden finden, der dir für lau alles vorkaut.
Nen Timer habe ich noch nie benutzt und weis auch nicht wie man den einstellt. und leider gibz auch nirgends beispiele wo es ersichtlich ist welcher befehl für was sinnvoll ist.
Hast du meinen Beitrag in der Rubrik "FAQ" gelesen: "keine Angst vor Assembler"? Natürlich nicht und in dem Forum "Mikrocontroller.net warst du vermutlich auch noch nicht und hast dich dort durch die angebotenen Tutorials gelesen. So ein bißchen Eigeniniative kann nicht schaden, denn niemand hier wird dir deine Arbeit machen. Helfen ja, aber wir haben neben diesem Forum noch andere Aufgaben zu erledigen. Wenn ich beispielsweise feststelle, das der Ratsuchende nur versucht, ne billige Informationsquelle zu finden, die eigene Arbeitsleistung spart, werd ich dann schon mal etwas direkt.
dann dieser CP Befehl. der Vergleicht die beiden Register (subtrahiert die Zahlen) soweit hab ichs gefunden. aber der nachfolgende Befehl "BRL0", hängt der mit dem CP befehl zusammen (so wie DEC und BRNE?) und was macht der dann genau. Zu End Timer ISR springen wenn cp=0?
CP=Compare = Vergleiche
Es ist richtig, der CP-Befehl ist eine Subttraktion, aber ohne das Ergebnis abzulegen, sondern nur um festzustellen, ob das Ergebnis negativ, positiv oder gar 0 ist.
BRLO=Branch if lower = verzweige, wenn kleiner ist doch klar.
Wenn mein Zähler im Register1 den Wert 45 hat und der Wert im Register "sechzig" = 60 ist, dann ist 45-50 -15 also negativ und damit ist das Statusbit gesetzt, welches dem Befehl BRLO sagt, "führe aus".
Dazu noch eines
CP Register1, Register2 = Vergleich zwischen zwei Registern
CPI Register1,60= Vergleich zwischen Register1 und einer festen Zahl
Allerdings, nicht alle Register können den CPI-Befehl, erst ab dem Register16 (R16) ist das möglich.
Wär super wenn du mir da weiter helfen könntest. -> Vorallem das mit dem Timer bei dem ich immer noch nicht weis was wie man das Ding eigendlich einstellt.
Gern, wenn du vorher deine Hausaufgaben machst und dir vielleicht ein wenig mehr Mühe beim Deutsch gibst und nicht dauernd eine eigene Sprachversion versuchst zu verbreiten.
Ach ja, und wiso hat jeder einen Code einfügen Knopf auser ich?
Hast du ach,mußt nur richtig hinsehen. Da gibts erst einmal ein Button "erweitert"....
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)