- 12V Akku mit 280 Ah bauen         
Seite 2 von 3 ErsteErste 123 LetzteLetzte
Ergebnis 11 bis 20 von 30

Thema: Mit Assembler eine Blinkschaltung bauen

  1. #11
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    05.09.2007
    Beiträge
    168
    Anzeige

    Powerstation Test
    Zitat Zitat von A.Hoffmann
    Gute tag zusammen.

    Um die Blinkdauer zu verändern gibt es zwei Routinen in dem Programm.
    wait_timer_1 ist ein 8 Bit Sekunden Zähler.
    Also
    ldi temp2, Verzöerungszeit in Sekunden ( max 255 )
    rcall wait_timer_1 ; warten

    oder den Timer

    wait_timer_6
    8 Bit milisekunden Zähler

    ldi temp2, milisekunden ( max 255 )
    rcall wait_timer_6 ; warten


    Mfg
    A.Hoffmann
    Vielen Dank für die genaue Beschreibung des Timers!
    Und danke für die Version mit der Ansteuerung für alle LED's ...
    Sowas habe ich heute mit Hilfe deines ersten Codes auch geschafft ...
    Nun werde ich die mal vergleichen ...

  2. #12
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    05.09.2007
    Beiträge
    168
    Kann es sein, dass wenn ich 255 ms warte, dass das zu schnell für den Roboter ist?

    EDIT:

    Habe meinen Fehler gefunden ...

    Da stand noch ldi temp2, 1 ...

  3. #13
    Moderator Robotik Einstein Avatar von damaltor
    Registriert seit
    28.09.2006
    Ort
    Milda
    Alter
    37
    Beiträge
    4.063
    der prozessor schafft ziemlich genau 8 millionen befehle pro sekunde (da die meisten befehle der avrs SINGLE-CYKLE (schreibt man das so?) sind.). in 255 millisekunden als immer noch gute 2 millionen befehle... =) da sieht man mal was 8 megahertz sind...
    Read... or die.
    ff.mud.de:7600
    Bild hier  

  4. #14
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    05.09.2007
    Beiträge
    168

    Re: Blinker

    Zitat Zitat von A.Hoffmann

    [...]

    Code:
    .include "m8def.inc"
    .def		temp	=	r16			; Arbeitsregister
    .def		temp2	=	r17			; Arbeitsregister
    
    .equ		CLOCK	=	8000000
    
    .dseg
    
    timer_1:	.byte		1	; 8 Bit
    timer_6:	.byte		1	; milisekunden Zähler
    
    .cseg
    
    .org 0x000
    		    rjmp	Prog_Initial ;	Interruptvektoren überspringen
    			reti				;	INT0addr - Externer Interrupt 0
    			reti				;	INT1addr - Externer Interrupt 1
    			reti		  		;	OC2addr  - Output Compare2
    			reti				;	OVF2addr - Overflow2
    			reti				;	ICP1addr - Input Capture1
    			reti				;	OC1Aaddr - Output Compare1A
    			reti				;	OC1Baddr - Output Compare1B
    			reti				;	OVF1addr - Overflow1
    			rjmp	delay		;	OVF0addr - Overflow0
    			reti				;	SPIaddr  - SPI
    			reti	    		;	URXCaddr - USART Receive Complete
    			reti				;	UDREaddr - USART Data Register Empty
    			reti				;	UTXCaddr - USART Transmit Complete
    			reti				;	ADCCaddr - Analog Digital wandler
    			reti				;	ERDYaddr - EEPROM 
    			reti				;	ACIaddr  - Analog Comparator
    			reti				;	TWIaddr  - Two - Wire Interface
    			reti				;	SPMaddr  - SPM complete
    
    
    Prog_Initial:
    
    ;*********************************************************
    ;                               Stack Pointer setzen     *
    ;*********************************************************
    
    			ldi		temp,	LOW(RAMEND)		; Stack Pointer low
    			out		SPL,	temp			; setzen
    			ldi		temp,	HIGH(RAMEND)	; Stack Pointer high
    			out		SPH,	temp 			; setzen
    
    ;********************************************************
    ;*                        Init  Timer / Counter 0       *
    ;********************************************************
    
    ;Preclaer Divisions Factor = 64
    
    			ldi		temp,	131			; Counter = (256 - 125)	
    			out		TCNT0,	temp ; nach 125 Takten erfolgt ein Überlauf
    			ldi		temp,	0b00000011	; CS00 + CS01 = 1	
    			out		TCCR0,	temp		; Prescaler Division = 64
    			in		temp,	TIMSK
    			ori		temp,	0b00000001	; Interrupt
    			out		TIMSK,	temp		; erlauben
    
    ;*********************************************************
    ;*                           Init LEDs                   *
    ;*********************************************************
    
    			sbi		DDRB,	DDB0		; Duo LED grün
    			sbi		DDRD,	DDD2		; Duo LED rot
    			cbi		PORTB,	PORTB0		; aus
    			cbi		PORTD,	PORTD2		; schalten
    			sbi		DDRD,	DDD6		; Front LED
    			cbi		PORTD,	PORTD6		; aus
    			sbi		DDRD,	DDD7		; Back LEDs
    			cbi		PORTD,	PORTD7		
    			sbi		DDRC,	DDC0
    			sbi		DDRC,	DDC1
    			cbi		PORTC,	PORTC1
    			cbi		PORTC,	PORTC1		; ausschalten
    			ldi		temp,	0x00
    			sts		timer_1,		temp	; max
    			sts		timer_6,		temp	; max 255 mili Sekunden
    
    
    			sei							; Interrupts erlauben
    			
    
    
    ;****************   Ende Init  ****************************	
    				  						
    Prog_Run:
    loop:
    			rcall	back_led_l_on;
    			rcall	back_led_r_on;
    			ldi		temp2,	1		; 1 sekunde nichts tun
    			rcall	wait_timer_1
    			rcall	back_led_l_off
    			rcall	back_led_r_off
    			ldi		temp2,	1		; 1 sekunde nichts tun
    			rcall	wait_timer_1
    
    			rjmp	loop
    
    
    ;*********************************************************
    ;*                  Interrupt Routine für Timer 0        *
    ;*********************************************************
    delay:		
    			push	ZH
    			push	ZL
    			push	temp2
    			push	temp
    			in		temp,	SREG
    			push	temp			; sichern
    			lds		temp,	timer_6	; Milisekunenden
    			inc		temp			; Zähler
    			sts		timer_6,	temp ; + 1
    			adiw	r25:r24,	1	; Counter + 1
    			ldi		temp,	 	0xE8 ;0xE8	; 0x03E8 = 1000
    			ldi		temp2,		0x03 ;0x03
    			cp		r24,	temp	; Counter Low / 16 Bit compare
    			cpc		r25,	temp2	; Counter High 
    			brne	next_step		; noch keine Sekunde vergangen
    			lds		temp,	timer_1	; erhöhe die Sekunden Zähler um 1
    			inc		temp
    			sts		timer_1,		temp
    			ldi		r24,	0
    			ldi		r25,	0
    
    next_step:
    			ldi		temp,	131		; Counter Register
    			out		TCNT0,	temp	; neu Vorbelegen							
    			pop		temp			; Register
    			out		SREG,	temp
    			pop		temp
    			pop		temp2
    			pop		ZL
    			pop		ZH				; wieder herstellen
    			reti
    			
    ;********************************************************
    ;*                Hintere LED rechts Ein                *
    ;********************************************************
    back_led_r_on:
    			sbi		DDRD,	DDD7		
    			cbi		PORTD,	PORTD7		
    			sbi		DDRC,	PORTC0
    			sbi		PORTC,	PORTC0
    			ret
    
    ;*******************************************************
    ;*                Hintere LED rechts Aus               *
    ;*******************************************************
    back_led_r_off:
    			cbi		PORTC,	PORTC0
    			ret
    
    ;******************************************************
    ;*                          Hintere LED links Ein     *
    ;******************************************************
    back_led_l_on:
    			sbi		DDRD,	DDD7		
    			cbi		PORTD,	PORTD7		
    			sbi		DDRC,	PORTC1
    			sbi		PORTC,	PORTC1
    			ret
    
    ;******************************************************
    ;*                       Hintere LED links Aus        *                        
    ;******************************************************
    back_led_l_off:
    			cbi		PORTC,	PORTC1
    			ret
    
    
    ;***************************************************************
    ;*                warten auf timer_1                  		   *
    ;*   in temp2 wird die  Verzögerungs Zeit in Sekunden übergeben*
    ;***************************************************************
    
    wait_timer_1:
    			push	temp2					; Register
    			push	temp
    			in		temp,	SREG
    			push	temp					; sichern
    			ldi		temp,	0x00			; timer_1	
    			sts		timer_1,		temp	; auf Null setzen
    w_t_1:
    			lds		temp,	timer_1
    			cp		temp,	temp2			; timer_1 => temp2
    			brsh	ret_t_1							; ja
    			rjmp	w_t_1		
    ret_t_1:
    			pop		temp			; Register
    			out		SREG,	temp
    			pop		temp
    			pop		temp2			; wieder herstellen
    			ret
    
    
    
    ;******************************************************************************
    ;*                warten auf timer_6                 						  *
    ;*   in temp2 wird die  Verzögerungs Zeit in mili Sekunden übergeben          *
    ;******************************************************************************
    wait_timer_6:
    			push	temp2					; Register
    			push	temp
    			in		temp,	SREG
    			push	temp					; sichern
    			ldi		temp,	0x00			; timer_6	
    			sts		timer_6,		temp	; auf Null setzen
    w_t_6:
    			lds		temp,	timer_6
    			cp		temp,	temp2			; timer_6 => temp2
    			brsh	ret_t_6					; ja
    			rjmp	w_t_6		
    ret_t_6:
    			pop		temp					; Register
    			out		SREG,	temp
    			pop		temp
    			pop		temp2					; wieder herstellen
    			ret
    Einen Dank nochmals an A.Hoffmann, aber kann es sein, dass der Zähler nicht ganz korrekt arbeitet?
    Am meisten bei kleineren Zahlenwerten?
    Ich habe zum Beispiel 1 Sekunde testen wollen, aber der braucht viel länger!

    Entschuldigung A.Hoffmann!
    Dein Programm arbeitet super!
    Ich habe nur das ldi zu spät gesetzt, dadurch entsteht eine Verzögerung ...

  5. #15
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    05.09.2007
    Beiträge
    168
    @A.Hoffmann:

    Ich habe mal ne Frage:
    Was bringt folgender Code:

    Code:
    ;*********************************************************
    ;*                  Interrupt Routine für Timer 0        *
    ;*********************************************************
    delay:      
             push   ZH
             push   ZL
             push   temp2
             push   temp
             in      temp,   SREG
             push   temp         ; sichern
             lds      temp,   timer_6   ; Milisekunenden
             inc      temp         ; Zähler
             sts      timer_6,   temp ; + 1
             adiw   r25:r24,   1   ; Counter + 1
             ldi      temp,       0xE8 ;0xE8   ; 0x03E8 = 1000
             ldi      temp2,      0xFF ;0x03
             cp      r24,   temp   ; Counter Low / 16 Bit compare
             cpc      r25,   temp2   ; Counter High
             brne   next_step      ; noch keine Sekunde vergangen
             lds      temp,   timer_1   ; erhöhe die Sekunden Zähler um 1
             inc      temp
             sts      timer_1,      temp
             ldi      r24,   0
             ldi      r25,   0
    
    next_step:
             ldi      temp,   131      ; Counter Register
             out      TCNT0,   temp   ; neu Vorbelegen                     
             pop      temp         ; Register
             out      SREG,   temp
             pop      temp
             pop      temp2
             pop      ZL
             pop      ZH            ; wieder herstellen
             reti

  6. #16
    Benutzer Stammmitglied
    Registriert seit
    05.07.2007
    Beiträge
    44
    Hallo H3IIGhost

    Für die Zeitmesser wird der Timer/Counter0 Verwendet.
    Er wird so Programmiert, daß er jede Millisekunde einen Interrupt auslöst.
    Wenn dieser Interrupt ausgelöst wird, dann wird die dazugehörige Interrupt
    Service Routine ausgeführt.
    Dieser Interrupt Vektor an der Adresse 9 ( Timer/Counter0 Overflow ) zeigt auf delay.
    In diesem Programmteil werden dann der timer_1 ( 8 Bit Sekunden
    Zähler 1 - 255 ) und der timer_6 ( 8 Bit Millisekunden Zähler 1 - 255 )
    um 1 erhöht. Wobei der timer_6 immer um 1 erhöht wird der timer_1 jedoch nur, wenn eine Sekunde Vergangen ist. Dafür wird in den Registern 25:24 ein Zähler mitgeführt der bis 1000 zählt.
    Dabei fällt mir auf in dem Programm ist ein Fehler,
    der Zähler geht von 0 - 1000, er müßte aber von 1 - 1000 Zählen.
    Die Zähler timer_1 und timer_ 6 werden von den
    Unterprogrammen wait_timer_1 und wait_timer_6 benutzt. Sie setzten den
    jeweiligen Zähler auf 0 und warten dann, bis der Zähler den in temp2
    übergebenen Wert erreicht hat.
    Dann kehren sie zum aufrufenden Programm zurück.

    MfG
    A.Hoffmann

  7. #17
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    05.09.2007
    Beiträge
    168
    Danke für die Antwort ...
    Ist ganz schön kompliziert ...

    Habe nun selber ein Programm geschrieben:

    Code:
    .include "m8def.inc"
    .def temp = r16 			;Arbeitsregister setzen
    .def flag = r17				;Flagregister setzen
    .equ TIMERWERT = 65535-976	;Timerwert auf etwas setzen
    
    
    ;********************************************************
    ;*                        Init  Timer / Counter 1       *
    ;********************************************************
    
             ldi    temp, (1 << CS10) | (1 << CS12) 		;Setze Prescalerwerte
    		 												;CS10 auf Null und CS11 und CS12 auf Eins Divisionsfaktor = 1024
    		 out	TCCR1B, temp 							;Übergebe den Wert an das Timer/Counter1 Control Register B
    
    ;********************************************************
    ;*                        Init  LED's                   *
    ;********************************************************
    
    		cbi		PORTD7, PD7
    		cbi		DDRD,	DDD7 ;Back-LEDs wirklich ausschalten		
    
    ;********************************************************
    ;*                        MAIN                          *
    ;********************************************************
    
    main:
    		rcall front_led_on
    		rcall timer_countdown
    		rcall front_led_off
    		rcall timer_countdown
    		rjmp main
    
    ;********************************************************
    ;*                        Funktionen                    *
    ;*                        **********                    *
    ;********************************************************
    
    ;********************************************************
    ;*                        Timer Countdown               *
    ;********************************************************
    timer_countdown:
    
    loop:
    		in 		temp,	TIFR
    		sbrs 	temp,	TOV1
    		rjmp loop
    		rcall	timer_reset
    		ldi		temp,	(1<<TOV1)
    		out 	TIFR,	temp
    		ret
    
    ;********************************************************
    ;*                        Timer Reset                   *
    ;********************************************************
    timer_reset:
    		ldi	temp, HIGH(TIMERWERT)
    		out	TCNT1H, temp
    		ldi	temp, LOW(TIMERWERT)
    		out	TCNT1L, temp
    		ret
    
    ;********************************************************
    ;*                        Front LED On                  *
    ;********************************************************
    front_led_on:
    		sbi PORTD, PD6
    		sbi DDRD, DDD6
    		ret
    
    ;********************************************************
    ;*                        Front LED Off                 *
    ;********************************************************
    front_led_off:
    		cbi DDRD, DDD6
    		ret
    Was mich da aber stört ist, dass er nicht sofort mit dem Blinken startet.
    Warum nicht?

    @A.Hoffmann:
    Kann man dein Programm auch auf die Art schreiben, wie ich meinen Timer benutze?

  8. #18
    Benutzer Stammmitglied
    Registriert seit
    05.07.2007
    Beiträge
    44
    Guten Tag H3IIGhost.

    Du hast keinen Stack eingerichtet.
    Das ist aber unbedingt Notwendig, denn bei einem rcall wird der Programmzähler, eine 16 Bit Adresse auf dem Stapel abgelegt.
    Bei einem ret wird der Programmzähler wieder hergestellt und das Programm geht an der Stelle weiter, die dem rcall Aufruf folgt.
    In etwa so.
    Anweisung: rcall front_led_on
    Aktion: Rücksprungadresse auf den Stack sichern und dann das Unterprogramm Ausführen.
    Am ende des Unterprogramms (ret) den Programmzähler wieder herstellen und mit der nächsten Anweisung im Programm fortfahren.
    Anweisung: rcall timer_countdown
    Ohne initialisierten Stackpointer ist es reiner Zufall, wo das Programm nach der Rückkehr aus dem Unterprogramm weitermacht.
    Außerdem wird das TOV1 Bit bei einem Timer Überlauf zwar gesetzt, aber nicht automatisch zurückgesetzt.
    Zurückgesetzt wird das Bit, auch wenn es sich Merkwürdig anhört, indem du eine 1 an die Adresse des Bits schreibst.
    Auserdem solltest du das Unterprogramm timer_reset vor main einmal aufrufen, ansonsten fängt der Timer bei Null an zu zählen.
    Du solltest auch deine Programme im AVR Studio im Debug Modus Testen, dann fallen dir solche Fehler direkt auf.

    Zur Frage zwei. Ja. In meinem Programm wird das Timer Overflow Flag nur durch ein Interrupt Programm behandelt.


    MfG
    A.Hoffmann

  9. #19
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    05.09.2007
    Beiträge
    168
    Danke danke ...
    Ich benutze auch das AVR Studio aber die Fehlermeldung vom Debugger habe ich wohl falsch interpretiert.
    Wärst du eigentlich so nett, dass du mir das mal auf meine Art schreibst?

    Und warum funktioniert folgendes Programm so langsam ...

    Code:
    .include "m8def.inc"
    
    .def	temp = r19
    .def	temp2 = r17
    .def	temp3 = r18
    
    		LDI R16, HIGH(RAMEND)	;setzt R16 auf den höchsten wert 1024 (0x0400)
    		OUT SPH, R16			;setzt es in Stack Pointer (zwischenspeicher)
    		LDI R16, LOW(RAMEND)	;setzt R16 + 95 (0x005F) also wird niedrigster wert dazu addiert
    		OUT SPL, R16			;setzt es in Stack Pointer (zwischenspeicher)
    
    	;Front LED an
    main:	
    		ldi temp2, 0x0F
    		rcall front_led_on
    		RCALL warte			; Aufruf des Unterprogramms "warte"
    		rcall front_led_off
    		RCALL warte			; Aufruf des Unterprogramms "warte"
    
    		rjmp main
    
    
    	;FRONT-LED an als Unterprogramm
    front_led_on:
    		SBI PORTD, PD6
    		SBI DDRD, DDD6
    		ret
    
    	;FRONT-LED aus als Unterprogramm
    front_led_off:
    		CBI DDRD, DDD6
    		ret
    
    	;Warteschleife als Unterprogramm
    warte:	
    		dec temp2
    		mov temp, temp2
    		
    		LDI R17, 0xFF	;setzt R17 auf 255
    		LDI R18, 0xFF	;setzt R18 auf 255
    loop1:	DEC R17			;
    		BRNE loop1		;wenn nicht gleich, springt zu loop2
    		DEC R18
    		BRNE loop1
    		DEC temp
    		BRNE loop1
    		
    		RET				;automatischer Rücksprung zum Hauptprogramm

  10. #20
    Benutzer Stammmitglied
    Registriert seit
    05.07.2007
    Beiträge
    44
    Hallo H3IIGhost.
    Warum ist das Programm so langsam?
    Dein Problem dürfte von dieser Routine verursacht werden.
    Zerlegen wir doch mal das Programm in seine Bestandteile.
    In Zeile 2 wird der in temp2 Übergebene Wert um 1 Vermindert
    Und in Zeile 3 in temp Übertragen. Temp enthält jetzt 0x0E .
    In den Zeilen 4 und 5 werden die Register 18:17 mit FF Vorbelegt.
    In den Zeilen 6 und 7 wird das Register 17 auf Null herunter gezählt.
    Danach wird in Zeile 8 das Register 18 um eins Vermindert.
    Wenn es nicht Null ist Verzweigt das Programm in der Zeile 9
    zu Zeile 6 ( loop1 ).
    Dort wird aber wieder Register 17 um eins Vermindert.
    0x00 - 1 = 0xFF
    Das hat zur Folge, daß das Register 17 jetzt wieder 0xFF enthält.
    Dadurch wird aber die Bedingung in der Zeile 7 wieder wahr
    und die ganze Schleife wird wiederholt. Das war doch sicherlich
    nicht Beabsichtigt ? (255 * 255) * 14 = 910350 Durchläufe.

    ;Warteschleife als Unterprogramm
    1: warte:
    2: dec temp2
    3: mov temp, temp2
    4: LDI R17, 0xFF ;setzt R17 auf 255
    5: LDI R18, 0xFF ;setzt R18 auf 255
    6: loop1: DEC R17
    7: BRNE loop1 ;wenn nicht gleich, springt zu loop2
    8: DEC R18
    9: BRNE loop1
    10: DEC temp
    11: BRNE loop1
    12: RET ;automatischer Rücksprung zum Hauptprogramm

    Nun zu deiner Zweiten Frage. Natürlich kann man das Programm
    anpassen, aber ich kann darin keinen Sinn erkennen.
    Du solltest im Gegenteil soviel wie möglich von
    Interrupt Routinen erledigen lassen. Da bleibt dir mehr Zeit für
    Zusätzliche Programmfunktionen. Auch sind diese Programme nur
    als Beispiel zu sehen.

    MfG
    A.Hoffmann

Seite 2 von 3 ErsteErste 123 LetzteLetzte

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

MultiPlus Wechselrichter Insel und Nulleinspeisung Conrad