PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Mit Assembler eine Blinkschaltung bauen



H3llGhost
13.09.2007, 09:12
Hallo Leute,

ich habe vor eine Blinkschaltung mit den LEDs D15 und D16 zu schreiben.
Auf Assemblerbassis ...
Ist das möglich?
Könntet ihr mir ein bisschen helfen?


.include "m8def.inc"

LDI r16, 0b00000011 ; Hier kommt der Kommentar ... :D <-- Lade Register 16 mit 01000000
OUT DDRC, r16 ; R16 wird nach DDRD (Datenrichtungsregister!) übertragen
OUT PORTC, r16

LDI r17, 0b11100000
OUT DDRD, r17

main: RJMP main

Für die Back-LEDs habe ich den obrigen Code benutzt ...
Nun soll abwechselnd die eine und dann die Andere leuchten, wie mache ich das?

Arexx-Henk
13.09.2007, 16:04
Hallo,

schreib dir mal so ein Program in 'C' und guck dann in die automatisch generierten assembler Tekst Dateien .lst or .lss im gleichen Verzeichnis wie die 'C' Datei.

Eine Warteschleiffe wie
for(i=0;i<...;i++){
for(j=0;j<...;j++){
}}
ist vermutlich notwendig um genugend verzogerung zu generieren. Die verzogerung kann mann ausprobieren oder ausrechnen...

Tip: im 'C' Datei schreib mal etwas Tekst dazu wie:
//xyz

da kann mann in die .lst/.lss Dateien einfach suchen auf "xyz"

Gruss,

Henk

A.Hoffmann
13.09.2007, 18:29
Guten tag.

Versuche es doch mal damit.

MfG
A.Hoffmann




.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

H3llGhost
20.09.2007, 09:21
Ja das funktioniert!
Danke ... :)

Aber wie kann ich die Geschwindigkeit des Blinkens ändern?
Und Sie haben so viel geschrieben!
Wann kommen die anderen LEDs dran?

EDIT:

Mich würde einfach nur noch interessieren, wie ich den Timer schneller machen kann ...

damaltor
20.09.2007, 10:12
der timer wird schneller durch einen kleineren prescaler...
hast du dich schonmal mit assembler beschäftigt?

H3llGhost
20.09.2007, 10:17
Ich bin am lernen ... :D

Ich habe nun einfach folgendes gemacht:


ldi temp2, 0x00 ;0x03


Und dann geht es auch schneller!
Wo genau stelle ich den Prescaler ein?

A.Hoffmann
20.09.2007, 10:31
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

A.Hoffmann
20.09.2007, 11:13
Hallo
Hier eine Programmversion, die alle LEDs des ASURO ansteuert.


.include "m8def.inc"
.def temp = r16 ; Arbeitsregister
.def temp2 = r17 ; Arbeitsregister

.equ CLOCK = 8000000

.dseg

timer_1: .byte 1 ; 8 Bit Sekunden Zähler
timer_6: .byte 1 ; 8 Bit 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 ;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_r_on
ldi temp2, 1 ; 1 sekunde nichts tun
rcall wait_timer_1
rcall back_led_r_off
rcall back_led_l_on
ldi temp2, 1 ; 1 sekunde nichts tun
rcall wait_timer_1
rcall back_led_l_off
rcall front_led_on
ldi temp2, 1 ; 1 sekunde nichts tun
rcall wait_timer_1
rcall front_led_off
rcall gruen_ein
ldi temp2, 1 ; 1 sekunde nichts tun
rcall wait_timer_1
rcall rot_ein
ldi temp2, 1 ; 1 sekunde nichts tun
rcall wait_timer_1
rcall gelb_ein
ldi temp2, 1 ; 1 sekunde nichts tun
rcall wait_timer_1
rcall duo_led_aus
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



;******************************************
;* DUO LED grün ein *
;******************************************
gruen_ein:
sbi PORTB, PORTB0
cbi PORTD, PORTD2
ret

;******************************************
;* UO LED rot ein *
;******************************************
rot_ein:
cbi PORTB, PORTB0
sbi PORTD, PORTD2
ret

;******************************************
;* DUO LED gelb ein *
;******************************************
gelb_ein:
sbi PORTB, PORTB0
sbi PORTD, PORTD2
ret

;******************************************
;* DUO LED Aus *
;******************************************
duo_led_aus:
cbi PORTB, PORTB0
cbi PORTD, PORTD2
ret

;******************************************
;* Front LED Ein *
;******************************************
front_led_on:
sbi PORTD, PORTD6
ret

;******************************************
;* Front LED Aus *
;******************************************
front_led_off:
cbi PORTD, PORTD6
ret



;******************************************
;* warten auf timer_1
;*temp2 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 *
;*temp2 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



MfG
A.Hoffmann

damaltor
20.09.2007, 12:44
hab mal die quote-tags in deinem beitrag durch code-tags ersetzt... quote=zitat, code=codebox. =)

A.Hoffmann
20.09.2007, 15:28
Hallo damaltor
Danke. Ich habe das in der Eile ganz übersehen.

MfG
A.Hoffmann

H3llGhost
20.09.2007, 20:24
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 ... ;)

H3llGhost
26.09.2007, 15:03
Kann es sein, dass wenn ich 255 ms warte, dass das zu schnell für den Roboter ist?

EDIT:

Habe meinen Fehler gefunden ... :D

Da stand noch ldi temp2, 1 ... ](*,)

damaltor
26.09.2007, 20:17
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...

H3llGhost
10.10.2007, 15:26
[...]



.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 ... ](*,)

H3llGhost
17.10.2007, 07:59
@A.Hoffmann:

Ich habe mal ne Frage:
Was bringt folgender 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

A.Hoffmann
17.10.2007, 13:47
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

H3llGhost
17.10.2007, 16:47
Danke für die Antwort ...
Ist ganz schön kompliziert ...

Habe nun selber ein Programm geschrieben:



.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?

A.Hoffmann
18.10.2007, 07:52
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

H3llGhost
18.10.2007, 09:24
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 ...



.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

A.Hoffmann
19.10.2007, 03:18
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

T.J.
19.10.2007, 12:15
Es ist natürlich immer von Nachteil, wenn man tausen NOPs verschwendet nur um ne LED blinken zu lassen. Aber zu Beginn kann er das ja erstmal so machen ;)

A.Hoffmann
19.10.2007, 15:15
Hallo Thomas
Man sollte aber auch immer die Möglichkeiten die der ATmega8 bietet ausnutzen, denn dafür wurden sie ja Eingebaut.
Aber hier geht es nich darum auf welche Art der Zähler Programmiert ist sondern, daß er nicht richtig Funktioniert.
Das Problem ist doch, daß der Programm Ablauf so nicht geplant war.
Wie er weiter oben anführte, sind die Wartezeiten zu lang.
Offensichtlich hatte er nur 14 Durchläufe geplant.
Ich habe ihn lediglich auf eine Fehlerquelle hingewiesen.

MfG
A.Hoffmann

H3llGhost
08.01.2008, 08:28
@ A.Hoffmann:

Was ist eigentlich die Bedeutung von ori?
Weil hin und wieder finde ich einen Code der lautet wie folgt:



ori temp, $20


Und ich verstehe einfach dann den Sinn von ori nicht!

damaltor
08.01.2008, 16:05
ori temp, $20
spechert in "temp" den wert "temp OR 0x20" bzw temp|0x20

H3llGhost
08.01.2008, 17:01
Danke, damaltor!

Wie muss ich dann folgenden Codeausschnitt verstehen?



[...]
cp PWMCount, ocr_1 ; Ist der Grenzwert für Led 1 erreicht
brlt OneOn
ori temp, $01

OneOn: cp PWMCount, ocr_2 ; Ist der Grenzwert für Led 2 erreicht
brlt TwoOn
ori temp, $02
[...]

von Klick (http://www.mikrocontroller.net/articles/AVR-Tutorial:_PWM)

damaltor
08.01.2008, 17:06
hrhr.. das kann ich dir nicht sagen, es wird auf jeden fall unter bestimmten bedingungen ein wert in temp entweder mit 0x02 oder mit 0x01 maskiert.

H3llGhost
08.01.2008, 17:12
Hmm ...
das hatte ich mir auch schon gedacht, aber mich würde interessieren, wonach der entscheidet ... xD

A.Hoffmann
09.01.2008, 11:06
Hallo H3iiGhost

Die Anweisung ori stellt eine binäre Oder Verknüpfung zwischen einem
Register ( temp ) und einem direkt Wert ( zb $20 ) her.
Das Ergebnis dieser Verknüpfung steht in dem Register.
Mit dieser Anweisung, werden Bits im Zielregister gesetzt.
Alle Bits die im Zielregister logisch 1 waren bleiben es und zusätzilch
werden noch alle Bits die in dem direkt Wert 1 sind auch in dem Zielregister auf 1 gesetzt.
Ich hoffe damit deine Frage beantwortet zu haben.
Mit freundlichen Grüßen
A. Hoffmann

H3llGhost
09.01.2008, 15:14
Ähmm ...
Das verwirrt mich ehrlich gesagt noch mehr ...

Aber danke für deine BEmühung!

wkrug
09.01.2008, 17:18
Versuchen wirs noch mal

ORI ist eine Abwandlung des Befehls OR.
Mit OR können die Werte von 2 Registern miteinander bitweise ODER verknüpft werden.
Beispiel:
OR r16,r17
Wenn im Register r16
0b00000001 = hex 01
stand, und im Register r17
0b00000100 = hex 04
stand, ist nach diesem Befehl der Wert
0b00000101 = hex 05
im Register r16 zu finden.
Es ist keine Addition der Werte auch wenn sich das hier zufällig so ergibt.

Der Befehl ORI macht im Prinzip das gleiche, nur das als zweiter Partner der Oder Verknüpfung eine Konstante und kein zusätzliches Register verwendet wird.