Forum: Mikrocontroller und Digitale Elektronik Zeitverzögerung von 800ms einbauen (Assembler)


von Jörg S. (josh79)


Angehängte Dateien:

Lesenswert?

Hallo Leute!!

Ich möchte eine Zeiverzögerung von einer knappen Sekunde (800ms) in mein 
Programm einbauen. Desweiteren soll das Programm auch erst dann mit dem 
Ablauf weiter machen, wenn die Flanke von High auf Low am Eingang 
(Taster) gewechselt hat. Verwirklichen möchte ich mit meinem Programm 
ein D-Flip-Flop. Taster drücken, LED an, Taster wieder drücken, LED 
wieder aus.

Ich bitte um leicht verständliche Antworten. Dies ist mein erstes 
Programm und ich verstehe noch nicht sonderbar viel von Assambler, weil 
ich mich erst seit ca. 5 Tagen intensiv mit dem Thema AVRs und Assembler 
befasse und außer in der Spate Elektronik, quasi keine Vorkenntnisse 
habe.

Danke!!

Hier der Code: (Im Anhang noch mal der Code mit Kommentaren)
1
;-----------------------------------------------------------------------
2
; Datum und Autor - So.28.06.2015 / Josh79
3
;-----------------------------------------------------------------------
4
5
;-----------------------------------------------------------------------
6
; Programm: Aufbau eines einfach D-Flip-Flops
7
;-----------------------------------------------------------------------
8
9
;-----------------------------------------------------------------------
10
; Definition des AVRs (In diesem Fall ist es ein ATMEGA8)
11
;-----------------------------------------------------------------------
12
13
.nolist
14
.include    "m8def.inc"    ; ATMEGA8 von Atmel
15
.list
16
17
;-----------------------------------------------------------------------
18
; Definition der Ein-und Ausgänge an den verschiedenen Ports
19
;-----------------------------------------------------------------------
20
21
    ldi  r16, 0b11111111     
22
    out  DDRB, r16     
23
24
    ldi  r16, 0b00000000    
25
    out  DDRD, r16     
26
27
;-----------------------------------------------------------------------
28
; Taktfrequenz festlegen
29
;-----------------------------------------------------------------------
30
31
.EQU    fq = 4000000      
32
33
;-----------------------------------------------------------------------
34
; PullUp-Widerstände aktivieren
35
;-----------------------------------------------------------------------
36
37
    ldi  r16, 0b11111111   
38
    out  PORTD, r16    
39
40
;-----------------------------------------------------------------------
41
; Hauptprogramm - Aufbau eines D-Flip-Flops. 
42
;-----------------------------------------------------------------------
43
LEDaus:    ldi  r16, 0b11111111    
44
          out  PORTB, r16    
45
46
;-----------------------------------------------------------------------
47
; Es geht los. Taster 1 drücken um die LED 1 anzuschalten    
48
;-----------------------------------------------------------------------
49
50
Taster1An:  sbic  PIND, 0      
51
    rjmp  Taster1An    
52
53
;-----------------------------------------------------------------------
54
;Taster 1 wurde gedrückt, die erste LED am Ausgang wird eingeschaltet  
55
;-----------------------------------------------------------------------
56
57
LEDan:    ldi  r16, 0b11111110    
58
    out  PORTB, r16   
59
60
;-----------------------------------------------------------------------
61
; Zeitverzögerung von 800 ms und abwarten der Low-Flanke von Taster 1
62
;-----------------------------------------------------------------------
63
64
Zeit:
65
66
;-----------------------------------------------------------------------
67
; Taster 1 erneut drücken, um die LED wieder auszuschalten     
68
;-----------------------------------------------------------------------
69
70
Taster1Aus:  sbic  PIND, 0      
71
    rjmp  Taster1Aus    
72
73
;-----------------------------------------------------------------------
74
; LED wird ausgeschaltet       
75
;-----------------------------------------------------------------------
76
77
LEDaus2:  ldi  r16, 0b11111111     
78
    out   PORTB, r16    
79
80
;-----------------------------------------------------------------------
81
; Zeitverzögerung von 800 ms und abwarten der Low-Flanke von Taster 1
82
;-----------------------------------------------------------------------
83
84
Zeit2:
85
86
;-----------------------------------------------------------------------
87
; Endlosschleife     
88
;-----------------------------------------------------------------------
89
90
Loop:    rjmp  Taster1An    
91
92
;-----------------------------------------------------------------------
93
; Ende       
94
;-----------------------------------------------------------------------
95
96
Ende:

: Bearbeitet durch User
von Inga (Gast)


Lesenswert?

Wo ist die Frage?
Wie bekommst du einen 800ms Delay hin?
http://electronics-lab.com/downloads/mcu/003/index.html
Da kannst du dir einen netten Warteschleifen Generator runterladen.
Ist aber nicht wirklich effizient, falls du noch Erweiterungen einplanen 
solltest, da der Prozessor während der 800ms nur blöde die Zeit 
verplempert. Mt einem Timer wärs etwas effizienter.
Falls es im Batteriebetrieb laufen soll, könnte man den Taster an einen 
INT0 oder 1 hängen und solange die LED aus ist und kein Taster gedrückt 
ist den µC in den Sleep-Modus versetzen. Der wacht dann bei Tasterdruck 
wieder auf.

von Inga (Gast)


Lesenswert?

Das kannst du dir sparen, da niemals im Programmablauf erreicht.

Jörg S. schrieb:
> ;-----------------------------------------------------------------------
> ; Ende
> ;-----------------------------------------------------------------------
>
> Ende:

von Inga (Gast)


Lesenswert?

1
;-----------------------------------------------------------------------
2
; Datum und Autor - So.28.06.2015 / Josh79
3
;-----------------------------------------------------------------------
4
5
;-----------------------------------------------------------------------
6
; Programm: Aufbau eines einfach D-Flip-Flops
7
;-----------------------------------------------------------------------
8
9
;-----------------------------------------------------------------------
10
; Definition des AVRs (In diesem Fall ist es ein ATMEGA8)
11
;-----------------------------------------------------------------------
12
13
.nolist
14
.include    "m8def.inc"    ; ATMEGA8 von Atmel
15
.list
16
17
;-----------------------------------------------------------------------
18
; Definition der Ein-und Ausgänge an den verschiedenen Ports
19
;-----------------------------------------------------------------------
20
21
    ldi  r16, 0b11111111
22
    out  DDRB, r16
23
24
    ldi  r16, 0b00000000
25
    out  DDRD, r16
26
27
;-----------------------------------------------------------------------
28
; Taktfrequenz festlegen
29
;-----------------------------------------------------------------------
30
31
.EQU    fq = 4000000
32
33
;-----------------------------------------------------------------------
34
; PullUp-Widerstände aktivieren
35
;-----------------------------------------------------------------------
36
37
    ldi  r16, 0b11111111
38
    out  PORTD, r16
39
40
;-----------------------------------------------------------------------
41
; Hauptprogramm - Aufbau eines D-Flip-Flops.
42
;-----------------------------------------------------------------------
43
LEDaus:    ldi  r16, 0b11111111
44
          out  PORTB, r16
45
46
;-----------------------------------------------------------------------
47
; Es geht los. Taster 1 drücken um die LED 1 anzuschalten
48
;-----------------------------------------------------------------------
49
50
Taster1An:  sbic  PIND, 0
51
    rjmp  Taster1An
52
53
;-----------------------------------------------------------------------
54
;Taster 1 wurde gedrückt, die erste LED am Ausgang wird eingeschaltet
55
;-----------------------------------------------------------------------
56
57
LEDan:    ldi  r16, 0b11111110
58
    out  PORTB, r16
59
60
;-----------------------------------------------------------------------
61
; Zeitverzögerung von 800 ms und abwarten der Low-Flanke von Taster 1
62
;-----------------------------------------------------------------------
63
64
Zeit:
65
; ============================= 
66
;    delay loop generator 
67
;     3200000 cycles:
68
; ----------------------------- 
69
; delaying 3199998 cycles:
70
          ldi  R17, $F1
71
WGLOOP0:  ldi  R18, $19
72
WGLOOP1:  ldi  R19, $B0
73
WGLOOP2:  dec  R19
74
          brne WGLOOP2
75
          dec  R18
76
          brne WGLOOP1
77
          dec  R17
78
          brne WGLOOP0
79
; ----------------------------- 
80
; delaying 2 cycles:
81
          nop
82
          nop
83
; ============================= 
84
85
86
87
88
;-----------------------------------------------------------------------
89
; Taster 1 erneut drücken, um die LED wieder auszuschalten
90
;-----------------------------------------------------------------------
91
92
Taster1Aus:  sbic  PIND, 0
93
    rjmp  Taster1Aus
94
95
;-----------------------------------------------------------------------
96
; LED wird ausgeschaltet
97
;-----------------------------------------------------------------------
98
99
LEDaus2:  ldi  r16, 0b11111111
100
    out   PORTB, r16
101
102
;-----------------------------------------------------------------------
103
; Zeitverzögerung von 800 ms und abwarten der Low-Flanke von Taster 1
104
;-----------------------------------------------------------------------
105
106
Zeit2:
107
108
109
; ============================= 
110
;    delay loop generator 
111
;     3200000 cycles:
112
; ----------------------------- 
113
; delaying 3199998 cycles:
114
          ldi  R17, $F1
115
WGLOOP01:  ldi  R18, $19
116
WGLOOP11:  ldi  R19, $B0
117
WGLOOP21:  dec  R19
118
          brne WGLOOP21
119
          dec  R18
120
          brne WGLOOP11
121
          dec  R17
122
          brne WGLOOP01
123
; ----------------------------- 
124
; delaying 2 cycles:
125
          nop
126
          nop
127
; ============================= 
128
129
130
131
132
;-----------------------------------------------------------------------
133
; Endlosschleife
134
;-----------------------------------------------------------------------
135
136
Loop:    rjmp  Taster1An

: Bearbeitet durch User
von Jörg S. (josh79)


Lesenswert?

Danke INGA,

das funktioniert schon mal, aber die Zeit, die nun vergeht, liegt bei 
ca. 3 Sekunden, bis das Programm weiter läuft. Wie kann ich die Zeit auf 
800ms oder von mir aus auch 1 sekunde anpassen??

Grüße, Jörg

von S. Landolt (Gast)


Lesenswert?

Dann wird wohl der ATmega8 nicht mit 4, sondern mit 1 MHz laufen. Also 
die Fuses entsprechend einstellen oder die Konstanten der Zeitschleife 
passend berechnen.

von Jörg S. (josh79)


Angehängte Dateien:

Lesenswert?

Habe es jetzt hinbekommen, indem ich ein wenig mit dem "delay loop 
generator" rumgespielt habe und die Werte veränderte. Ich verstehe zwar 
in keiner Weise was der Generator macht und wodurch genau der Code die 
Verzögerung verursacht, aber es funktioniert.

Dass das Programm erst weiterläuft, sobald der Taster nach der 
Zeitverzögerung losgelassen wird, habe ich auch verwirklichen können. 
Der vollständigkeisthalber lege ich noch mal den fertigen Code als Datei 
bei.

Danke, Jörg

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

> Ich verstehe zwar in keiner Weise ...

Ihr Controller läuft mit 1 MHz, also benötigt 1 Takt die Zeit von 1 µs. 
Der Befehl dec benötigt 1, brne 2 Takte.

von Jörg S. (josh79)


Lesenswert?

Und was sollen die 3 Register (R17-R19). Und was machen die 
"$"-Zeichen?!

von foo (Gast)


Lesenswert?

Jörg S. schrieb:
> Habe es jetzt hinbekommen, indem ich ein wenig mit dem "delay loop
> generator" rumgespielt habe und die Werte veränderte. Ich verstehe zwar
> in keiner Weise was der Generator macht und wodurch genau der Code die
> Verzögerung verursacht, aber es funktioniert.

Sind drei verschachtelte Schleifen. Die innerste zählt 0x92 ($ = 
Hexadezimalwert folgt) mal r19 runter, die mittlere zählt dabei r18 
runter (6 Mal) und die äußerste wird 0x97 mal ausgeführt und zählt dabei 
r17 runter. Ist r17 null hört der Kram auf.

Die Zahl der Zyklen entsteht dadurch dass dec+brne (wenn der Sprung 
genommen wird, was bei fast allen Durchläufen der Fall ist) drei Zyklen 
braucht.

((0x92*3 + 3) * 0x06 + 3) * 0x97 = 3999999

Die +3 sind dabei die drei Zyklen vom dec+brne der jeweils nächstäußeren 
Schleife, die dann noch draufkommen.

von S. Landolt (Gast)


Lesenswert?

dec muss in einem Register ablaufen. '$' ist eine andere Schreibweise 
für '0x', das Nachfolgende wird vom Assembler also als Hexadezimalzahl 
gelesen.

von S. Landolt (Gast)


Lesenswert?

Haben Sie sich auf der Artikelseite des Forums schon die 'AVR Assembler 
...'-Artikel angeschaut?

von Oliver H. (Firma: OliverHeinrichs.de) (dobson)


Lesenswert?

Mal auf die schnelle... Aber ungetestet und du müsstest es noch auf den 
µC und auf den Takt anpassen (Fuses lernen!!! Sei vorsichtig). Sollte 
aber funktionieren.
Die Delay-Routine ist nicht 100% da ich vergessen habe die Takte für 
rcall und ret abzuziehen.
Aber im groben und ganzen sind das mit Toleranz 800ms. ;-)
1
;============================
2
;= 800msFF            =
3
;= (C) by Oliver Heinrichs  =
4
;============================
5
6
;---------- Prozessordefinition -------------------------
7
.include  "tn85def.inc"
8
9
;---------- Taktfrequenz --------------------------------
10
.equ XTAL  = 8000000
11
12
;---------- Register-Belegung ---------------------------
13
;Arbeitsregister
14
.def TMP1  = r16
15
.def TMP2  = r17
16
.def TMP3  = r18
17
18
;---------- I/O-Definitionen ----------------------------
19
.equ PORT  = PORTB
20
.equ PIN  = PINB
21
.equ DDR  = DDRB
22
.equ TASTER = 3
23
.equ LED  = 4
24
25
;---------- Sprungmarken --------------------------------
26
.org 0x0000
27
    rjmp    init
28
29
;---------- Initialisierung und Konfiguration -----------
30
init:
31
  ;Stack-Pointer initialisieren
32
  ldi   TMP1, LOW(RAMEND)
33
  out   SPL, TMP1
34
  ldi   TMP1, HIGH(RAMEND)
35
  out   SPH, TMP1
36
37
  ;I/O konfigurieren
38
  ;Taster auf 0 --> Eingang - LED auf 1 -- Ausgang
39
  ldi    TMP1, (0<<TASTER) | (1<<LED)
40
  out    DDR, TMP1
41
  ;Taster auf Pullup - LED auf 1 da invertiert benötigt
42
  ldi    TMP1, (1<<TASTER) | (1<<LED)
43
  out    PORT, TMP1
44
45
;---------- Hauptschleife -------------------------------
46
;Warten auf Taster
47
loop:
48
  sbic  PIN, TASTER  ; Den nächsten Befehl überspringen, sofern der Taster 1 gedrückt ist
49
  rjmp  loop    ; Taste war nicht gedrückt also zurück zu "Taster1"
50
loop1:
51
  ldi    TMP1, (0<<LED)
52
  out    PORTB, TMP1    ; Setz Ausgang auf low. Die LED müsste leuchten
53
loop2:
54
  ;Warteschleife vom AVR Delay Calc leicht angepasst
55
  rcall  delay
56
loop3:
57
  sbic  PIN, TASTER    ;Frage taster ab
58
  rjmp  loop3      ; Ansonsten geht es zurück
59
loop4:
60
  ;Schalte LED aus
61
  ldi    TMP1, (0<<LED)
62
  out    PORTB, TMP1    ; Setz Ausgang auf High- Die LED müsste erlöschen
63
loop5:
64
  ;Warteschleife vom AVR Delay Calc leicht angepasst
65
  rcall  delay
66
loop6:
67
  ;Zurück zum Anfang
68
  rjmp  loop
69
70
;---------- Funktionen -----------------------------------
71
delay:
72
; ============================= 
73
;   Warteschleifen-Generator 
74
;     6400000 Zyklen:
75
; ----------------------------- 
76
; warte 6399996 Zyklen:
77
delay_0:  ldi  TMP2, $35 ;Hexadezimale schreibweise
78
delay_1:  ldi  TMP3, $A6
79
delay_2:  dec  TMP3
80
          brne delay_2
81
          dec  TMP2
82
          brne delay_1
83
          dec  TMP1
84
          brne delay_0
85
; ----------------------------- 
86
; warte 3 Zyklen:
87
          ldi  TMP1, $01
88
delay_3:  dec  TMP1
89
          brne delay_3
90
; ----------------------------- 
91
; warte 1 Zyklus:
92
          nop
93
; ============================= 
94
    ret

: Bearbeitet durch User
von Oliver H. (Firma: OliverHeinrichs.de) (dobson)


Lesenswert?

Ach ja. Wenn du einen anderen Takt als 8MHz hast musst du natürlich auch 
die delay-Routine anpassen, da die in Takten rechnet und nicht nach 
Zeit.Ist aber nur Copy`n`Paste... ;) Das Programm hast ja jetzt. Ich 
sollte mich aber Inga anschließen und dir die Verwendung eines Timers in 
Verbindung mit dem Sleep-Mode nahelegen. Einmal aus Lern- und auch aus 
Energiespargründen.

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

Pardon, aber...:

>  ldi    TMP1, (0<<LED)
>  out    PORTB, TMP1    ; Setz Ausgang auf low. Die LED müsste leuchten

>  ldi    TMP1, (0<<LED)
>  out    PORTB, TMP1    ; Setz Ausgang auf High- Die LED müsste erlöschen

??

Und jeweils direkt im Anschluss §delay aufzurufen, die von TMP1 abhängig 
ist?

von Oliver H. (Firma: OliverHeinrichs.de) (dobson)


Lesenswert?

Ja. Na und?
Das Register kann doch ruhig überschrieben werden, da die Werte nicht 
mehr benötigt werden. Werden doch nach Ablauf der Zeitschleife neu 
geladen.

von S. Landolt (Gast)


Lesenswert?

Die Zeitverzögerung in delay hängt von TMP1 ab, ist also eigentlich 
undefiniert. Das fällt spätestens dann auf, wenn die jetzt falsche 
Ausgabensteuerung korrigiert wird.

von foo (Gast)


Lesenswert?

>>  ldi    TMP1, (0<<LED)
>>  out    PORTB, TMP1    ; Setz Ausgang auf low. Die LED müsste leuchten
>
>>  ldi    TMP1, (0<<LED)
>>  out    PORTB, TMP1    ; Setz Ausgang auf High- Die LED müsste erlöschen

Also für auf high setzen würde ich auch nicht 0<<LED in den Port 
schieben.

Aber was, wenn an einem anderen Pin von PORTB noch was dranhängt, was 
nicht geändert werden soll? Ich würde das auf sbi/cbi ändern. Dann wird 
wirklich nur das eine Bit geändert und der Rest bleibt unangetastet.

Irgendwann wird was an einen anderen Pin von PORTB gehangen und wenns 
erst im nächsten Projekt ist. Aber dann hat man sich bis dahin schon 
gemerkt, dass man Bits einzeln ändern kann, und nicht das komplette 
Portregister überschreiben muss.

von Oliver H. (Firma: OliverHeinrichs.de) (dobson)


Lesenswert?

foo schrieb:
> Aber was, wenn an einem anderen Pin von PORTB noch was dranhängt, was
> nicht geändert werden soll? Ich würde das auf sbi/cbi ändern. Dann wird
> wirklich nur das eine Bit geändert und der Rest bleibt unangetastet.

Stimmt. Hast du Recht. Mein Codebeispiel sollte aber auch nur als 
Beispiel dienen.

foo schrieb:
> Irgendwann wird was an einen anderen Pin von PORTB gehangen und wenns
> erst im nächsten Projekt ist. Aber dann hat man sich bis dahin schon
> gemerkt, dass man Bits einzeln ändern kann, und nicht das komplette
> Portregister überschreiben muss.

s.o.

foo schrieb:
> Also für auf high setzen würde ich auch nicht 0<<LED in den Port
> schieben.

Siehe ersten Beitrag mit Code. Da kam es mir invertiert vor...

von Oliver H. (Firma: OliverHeinrichs.de) (dobson)


Lesenswert?

S. Landolt schrieb:
> Die Zeitverzögerung in delay hängt von TMP1 ab, ist also eigentlich
> undefiniert.

Oooops. Den Fehler seh ich auch erst jetzt. Dankeschön. Eigentlich 
sollte die Schleife so lauten:
1
;---------- Funktionen -----------------------------------
2
delay:
3
; ============================= 
4
;   Warteschleifen-Generator 
5
;     6400000 Zyklen:
6
; ----------------------------- 
7
; warte 6399996 Zyklen:
8
          ldi  TMP1, $F1
9
delay0:  ldi  TMP2, $35
10
delay1:  ldi  TMP3, $A6
11
delay2:  dec  TMP3
12
          brne delay2
13
          dec  TMP2
14
          brne delay1
15
          dec  TMP1
16
          brne delay0
17
; ----------------------------- 
18
; warte 3 Zyklen:
19
          ldi  TMP1, $01
20
delay3:  dec  TMP1
21
          brne delay3
22
; ----------------------------- 
23
; warte 1 Zyklus:
24
          nop
25
      ;Zurück
26
      ret
27
; =============================
Wie gesagt. Ungetestet. Und da hatte sich wohl eine Zeile geweigert... 
Das kommt davon, wenn man Code ungeprüft ins Netz stellt.

: Bearbeitet durch User
von Oliver H. (Firma: OliverHeinrichs.de) (dobson)


Lesenswert?

Jetzt hab ich noch nen Fehler meinerseits entdeckt... Die LED bleibt 
immer aus.
Der komplette bereinigte Code sollte lauten:
1
;============================
2
;= 800msFF            =
3
;= (C) by Oliver Heinrichs  =
4
;============================
5
6
;---------- Prozessordefinition -------------------------
7
.include  "tn85def.inc"
8
9
;---------- Taktfrequenz --------------------------------
10
.equ XTAL  = 8000000
11
12
;---------- Register-Belegung ---------------------------
13
;Arbeitsregister
14
.def TMP1  = r16
15
.def TMP2  = r17
16
.def TMP3  = r18
17
18
;---------- I/O-Definitionen ----------------------------
19
.equ PORT  = PORTB
20
.equ PIN  = PINB
21
.equ DDR  = DDRB
22
.equ TASTER = 3
23
.equ LED  = 4
24
25
;---------- Sprungmarken --------------------------------
26
.org 0x0000
27
    rjmp    init
28
29
;---------- Initialisierung und Konfiguration -----------
30
init:
31
  ;Stack-Pointer initialisieren
32
  ldi   TMP1, LOW(RAMEND)
33
  out   SPL, TMP1
34
  ldi   TMP1, HIGH(RAMEND)
35
  out   SPH, TMP1
36
37
  ;I/O konfigurieren
38
  ;Taster auf 0 --> Eingang - LED auf 1 -- Ausgang
39
  ldi    TMP1, (0<<TASTER) | (1<<LED)
40
  out    DDR, TMP1
41
  ;Taster auf Pullup - LED auf 1 da invertiert benötigt
42
  ldi    TMP1, (1<<TASTER) | (1<<LED)
43
  out    PORT, TMP1
44
45
;---------- Hauptschleife -------------------------------
46
;Warten auf Taster
47
loop:
48
  sbic  PIN, TASTER  ; Den nächsten Befehl überspringen, sofern der Taster 1 gedrückt ist
49
  rjmp  loop    ; Taste war nicht gedrückt also zurück zu "Taster1"
50
loop1:
51
  ldi    TMP1, (0<<LED)
52
  out    PORTB, TMP1    ; Setz Ausgang auf low. Die LED müsste leuchten
53
loop2:
54
  ;Warteschleife vom AVR Delay Calc leicht angepasst
55
  rcall  delay
56
loop3:
57
  sbic  PIN, TASTER    ;Frage taster ab
58
  rjmp  loop3      ; Ansonsten geht es zurück
59
loop4:
60
  ;Schalte LED aus
61
  ldi    TMP1, (1<<LED) ;----------Hier war der Fehler
62
  out    PORTB, TMP1    ; Setz Ausgang auf High- Die LED müsste erlöschen
63
loop5:
64
  ;Warteschleife vom AVR Delay Calc leicht angepasst
65
  rcall  delay
66
loop6:
67
  ;Zurück zum Anfang
68
  rjmp  loop
69
70
;---------- Funktionen -----------------------------------
71
delay:
72
; ============================= 
73
;   Warteschleifen-Generator 
74
;     6400000 Zyklen:
75
; ----------------------------- 
76
; warte 6399996 Zyklen:
77
         ldi  TMP1, $F1
78
delay_0:  ldi  TMP2, $35 ;Hexadezimale schreibweise
79
delay_1:  ldi  TMP3, $A6
80
delay_2:  dec  TMP3
81
          brne delay_2
82
          dec  TMP2
83
          brne delay_1
84
          dec  TMP1
85
          brne delay_0
86
; ----------------------------- 
87
; warte 3 Zyklen:
88
          ldi  TMP1, $01
89
delay_3:  dec  TMP1
90
          brne delay_3
91
; ----------------------------- 
92
; warte 1 Zyklus:
93
          nop
94
; ============================= 
95
    ret

Was jetzt noch offen wäre ist... Was ist wenn man den Taster länger als 
800ms drückt, bzw. gedrückt hält. dann würde der Ausgang mit 800ms 
toggeln.
Also doch der Vorschlag von Inga oder eine weitere Abfrage welche 
abfragt ob der Taster losgelassen wurde und erst danach bei loop3 
weitermacht. Dabei würde aber auch keine Taster-Entprellung 
softwareseitig benutzt...
Schau dir mal das AVR-Tutorial an. Da wird so etwas hervorragend 
erklärt.

: Bearbeitet durch User
von Christian (dragony)


Lesenswert?

Ich finde die hier verwendete Zeitroutine wirklich SEHR unintuitiv. Das 
heisst nicht, dass sie nicht funktioniert. Nur arbeiten würde ich so 
nicht wollen. Hier ist meine eigene, die ich für meine Projekte 
verwende:
1
.macro delayms ms //Benötigt InsertDelayMs. 6 Bytes pro Benutzung. Maximal 65535ms.
2
  ldi r24,lo8(\ms)
3
  ldi r25,hi8(\ms)
4
  rcall delayms
5
.endm
6
7
.macro InsertDelayMs khz //Da push/pop/ret nicht beachtet werden, ist die Routine immer 10-11 Ticks länger als gewünscht.
8
delayms:
9
  push XL
10
  push XH
11
  delayanotherms:
12
    ldi XL,lo8(\khz / 4 - 2) ;Max 256 MHz CPU.
13
    ldi XH,hi8(\khz / 4 - 2)
14
    delayonems:;
15
      sbiw XL,1
16
      brne delayonems
17
    rjmp .+0
18
    sbiw r24,1
19
    brne delayanotherms
20
  pop XH
21
  pop XL
22
  ret
23
.endm

Wenn ich in meinem Projekt eine 1 MHZ MCU verwende, muss ich die 
"Funktion" nur einmal einbinden und kann kann mit dem zweiten Macro 
beliebig viele Delays in den Code einbauen, wobei jeder Delay 6 Bytes 
Code belegt.
1
KHZ = 1000
2
3
mainloop:
4
       blabla code
5
       delayms 800
6
       mehr code
7
       delayms 100
8
       rjmp mainloop
9
10
InsertDelayMs KHZ

Die Funktion ist jedoch immer +-10 Ticks länger, da ret und push nicht 
abgezogen werden. Warum "10-11"? Je nach MCU braucht die eine mehr oder 
weniger Ticks für ret, push oder pop. Ich möchte aber nicht für jede MCU 
eine eigene delayms bauen, da die gegebene Genauigkeit für eine delayms 
Funktion ausreichend ist. Für taktgenaue Funktionen habe ich andere 
Macros.

Wer mag, kann beim ersten Macro noch die Register r24 und r25 sichern. 
Ich arbeite jedoch so, dass die Register 24 und 25 generell immer als 
"unsicher" gelten und ich immer davon ausgehe, dass diese bei 
Macroaufrufen zerstört werden.

: Bearbeitet durch User
von oldmax (Gast)


Lesenswert?

Hi
Nun, du solltest dir abgewöhnen, mit Delays die Zykluszeit 
hochzuschrauben. Statt dessen befasse dich mit dem Timer und leite dir 
einen ms, 10 ms oder 1s Signal ab. Das ist nicht besonders schwer. Das 
Signal ist eigentlich nichts anderes wie en Bit. Ist es gesetzt, dann 
rufst du eine Routine auf, die das Ereignis bearbeitet und das Bit 
zurücksetzt. So reagiert dein Zyklus immer nur auf dieses Ereignisbit. 
Angenommen, im Byte "TimeFlag" setzt du im Timerinterrupt das Bit 0 jede 
ms, das Bit 2 alle 10 ms, das Bit 2 alle 100ms und das Bit 3 jede 
Sekunde. Dafür benötigst du entsprechend Zähler, die du in der ISR dann 
prüfst. Diese gesetzten Bits sind dann deine Ereignisflags. In einem 
Beitrag der AVR-Praxis-Gemeinde steht das ziemlich genau beschrieben. 
Such mal nach "keine Angst vor Assembler" im Forum von AVR_Praxis unter 
FAQ. Dort solltest du weitere Infos finden, was du hier in den Tutorials 
vielleicht vermisst.
Gruß oldmax

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Du kannst auch die geniale Delay Routine von Steve Wozniak verwenden. 
Die ist recht ressourcensparend und erlaubt sehr grosse Variationen der 
Zeit:
1
; wozwait
2
.equ  MS10    = 0x34     ; 10ms 
3
.equ  MS30    = 0x60     ; 30ms
4
.equ  MS100    = 0x72    ; roughly 100mS with wozwait @ 8Mhz
5
.equ  second    = 0xe6    ; roughly a second with wozwait @ 8Mhz
6
.def  del  = r20  ; delay counter
7
8
; steve wozniak's wait routine - this time for AVR 
9
; this is the  routine with del^2 
10
; this wait routine is ingenious and handles wide ranges of delay 
11
; as a hommage to steve i port it to all my embedded systems. 
12
; some presets
13
w10ms:  ldi  del,MS10
14
  rjmp  wozwait
15
w30ms:  ldi  del,MS30
16
  rjmp  wozwait
17
w100ms:  ldi  del,MS100
18
;
19
wozwait: push  del
20
wwait2:  push  del
21
wwait1:                ; if the routine should be interuptible, insert your criteria here
22
; sbis  PINB, button   ; e.g. a pin state
23
; rjmp  enddelay       ; end the routine and pop everything back into order
24
  subi  del,1  
25
  brne  wwait1
26
  pop  del
27
  subi  del,1
28
  brne  wwait2
29
  pop  del
30
  subi  del,1
31
  brne  wozwait
32
  ret
33
enddelay:
34
  pop  del
35
  pop  del
36
  ret
Die Konstanten klingele ich einmal mit dem Simulator durch.

: Bearbeitet durch User
von Moby (Gast)


Lesenswert?

oldmax schrieb:
> Nun, du solltest dir abgewöhnen, mit Delays die Zykluszeit
> hochzuschrauben. Statt dessen befasse dich mit dem Timer

Das kann man nur dick unterstreichen.
Alles andere ist reichlich laienhaft, ineffizient + wenig universell 
verwendbar- und führt früher oder später zu Problemen.
Für alle Problemchen dieser Art und noch sehr viel mehr sollte man /in 
allen Programmen/ ganz grundsätzlich einen festen Timerinterupt 
installieren, z.B. alle 100ms. Damit und darin lassen sich nicht nur 
Verzögerungen elegant umsetzen, sondern noch Tausend andere Aufgaben 
mehr, mit denen sich ein Hauptprogramm dann nicht mehr rumzuschalgen 
hat. Zum Beispiel Tasterabfragen samt Entprellung oder 
Ausgaben/Initialisierungen aller Art. Das Hauptprogramm fragt nur noch 
Flags oder auch Datenbytes ab oder setzt diese- Datenfelder die damit 
das Interface zum "Mini-Betriebssystem" Timerinterrupt darstellen.

So strukturiert man, so vereinfacht man, so optimiert man 
Ressourcenverbrauch, so schafft man Übersicht!

von Peter D. (peda)


Lesenswert?

Oliver H. schrieb:
> ldi  TMP1, $F1
> delay_0:  ldi  TMP2, $35 ;Hexadezimale schreibweise
> delay_1:  ldi  TMP3, $A6

Ich verstehe nicht, wozu man sich kryptische Hex-Werte antut. Eine 
Änderung und sie müssen neu berechnet werden.

Der Assembler kann Konstanten doch selber ausrechnen.
Allerdings kann er kein float.
1
.nolist
2
.include "tn25def.inc"
3
4
        .equ    F_CPU = 8000000         ; 8MHz
5
6
.macro  warte
7
        ldi     r16, byte1( @0 / 6 )
8
        ldi     r17, byte2( @0 / 6 )
9
        ldi     r18, byte3( @0 / 6 )
10
        ldi     r19, byte4( @0 / 6 )
11
_warte11:
12
        subi    r16, byte1(1)           ; 1 Zyklus
13
        sbci    r17, byte2(1)           ; 1 Zyklus
14
        sbci    r18, byte3(1)           ; 1 Zyklus
15
        sbci    r19, byte4(1)           ; 1 Zyklus
16
        brne    _warte11                ; 2 Zyklen
17
                                        ; = 6 Zyklen
18
.endmacro
19
.list
20
.listmac
21
22
main:
23
        sbi     ddrb, pb1
24
        sbi     portb, pb1
25
        warte   F_CPU * 5 / 10          ; 0.5s
26
        cbi     portb, pb1
27
        warte   F_CPU * 1               ; 1s
28
        rjmp    main

von oldmax (Gast)


Lesenswert?

Hi
Moby schrieb:
> Alles andere ist reichlich laienhaft, ineffizient + wenig universell
> verwendbar- und führt früher oder später zu Problemen.

Na ja, ganz so hart würd ich es nicht sagen, obwohl es stimmt. Aber Jörg 
ist noch Anfänger und diesen Fehler machen wohl alle. Es ist auch gar 
nicht so einfach, sich eine Verzögerung vorzustellen, auf die man nicht 
warten muss oder anders herum gesagt, das eine Verzögerung Auch 
verhindert, das der Rst vom Programm bearbeitet wird. Ergebis sind dann 
völlig überflüssige Abfragen von Tastern in einem Interrupt, weil ja zu 
befürchten ist, das ein Tastendruck sonst nicht wahr genommen wird. Der 
trick, eine kurze Zykluszeit zu bekommen und hier rede ich von µs, ist 
in der Ereignisbearbeitung zu finden. Selbst ausgereifte und 
umfangreiche Programme lassen sich mit Job- oder Eventbits schnell und 
effektiv ausführen. Und auch ohne die Übersichtlichkeit zu verlieren. In 
dem angegebenen Beitrag hab ich dieses Problem ziemlich ausführlich 
beschrieben, so das ich hier auf weitere Programmcodes verzichte.
Gruß oldmax

von oldmax (Gast)


Lesenswert?

Hi
@Peter Dannegger
Ich nehme an, dein Beispiel bezieht sich auf das Setzen von Werten in 
Variablen und ist kein ernst gemeinter Beitrag zu Wartezeiten. Denn 
genau dieses Konstrukt legt den Controller für die Schleifendurchläufe 
auf Eis und dann sind Ereignisse, wie gedrückte Taster wiederum nur 
sicher zu erfassen, wenn das mit einer ISR bearbeitet wird.
Gruß oldmax

von oldmax (Gast)


Lesenswert?

Hi
Ach übrigends, aus AVR-Praxis ist MakerConnect geworden.
Gruß oldmax

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.