Pause in AVR-Studio mit variable?

Gast #353470
Lesenswert?

> mann kann ja einfach "delay50ms:" schreiben,...

Türlich kann Mann das. Ich glaub Frau kanns u.U. sogar auch;-) Ob es
das tut, was Du willst, weiß ich nicht.

Da musst Du Dir halt ne Zählschleife programmieren, die bei dem Takt,
mir dem Dein µC läuft, genau eine ms braucht und dann Deine Variable
runterzählen. Zu dem Thema ('wie berechne ich ne Zählschleife') gibt
hier ca. 1357,4 Threads. Kannst ja mal suchen. Aber vielleicht bringt
es mehr, wenn Du selber überlegst, wie es geht. Schau mal ins
Instruction Set Manual. Da steht drin, wie viele Taktzyklen die
einzelnen Befehle brauchen (für Dich kämen da am eheseten dekrement-
und Sprungbefehle in Frage...). Dann alles zusammenpuzzeln und
irgendwann läufts vielleicht sogar richtig.
Gast #353472
Lesenswert?

...

.def  pause_counter  = r25

...

rcall pause

...

;********Unterprogramme**********
pause:
 ldi pause_counter, 0xFF
 pause_loop:
  dec pause_counter
  nop
 brne pause_loop
 ret

Je nach MHz und dem Wert im counter, ändert sich die Verweildauer.
Also nachschauen wieviel Zyklen jeder einzelne Befehl benötigt, dann
zusammenaddieren. Der Sprung kostet nochmals 3 Zyklen.

Näheres gibt's hier:

http://www.avr-asm-tutorial.net/avr_de/beginner/sprung.html#Timing
Gast #353473
Lesenswert?

im myAVRWorkpad PLUS hab ich für sowas nen CodeWizard ich muss sagen
wier schnell mein AVR läuft und dann baut mir der Wizard die
warteroutine... hier bei 3,6864 MHz:

;--------------------------------------------------------------------
; myWait_us - Warte-Routine für x-Mikrosekunden einschließlich des
Aufrufes
; berechnete Abweichung: 8.5%
; PE: r16 = Anzahl der zu wartenden Mikrosekunden
; es wird mindestens 3µs gewartet, auch wenn r16 kleiner als 3
; PA: r16 = 0
;--------------------------------------------------------------------
; created by myAVR-CodeWizard
;--------------------------------------------------------------------
myWait_us:
   nop   ; wait
   subi   r16,3
   brcs   myWait_us_1
   breq   myWait_us_1
myWait_us_2:
   nop   ; wait
   dec   r16
   brne   myWait_us_2
myWait_us_1:
   ret
;--------------------------------------------------------------------

gruß Jahn
Gast #353474
Lesenswert?

hier noch mal ne millisekunden routine bei 3,6864 MHz aus dem CodeWiz

;--------------------------------------------------------------------
; myWait_ms - Warte-Routine für x-Millisekunden
; ein Millisekundenzyklus dauert 1,052 ms
; PE: r16 = Anzahl der zu wartenden Milisekunden
; PA: r16 = 0
;--------------------------------------------------------------------
; created by myAVR-CodeWizard
;----------------------------------------------------------------------- 
--
myWait_ms:
   push   r16
   ldi   r16,1
myWait_ms_3:
   push   r16
   ldi   r16,5
myWait_ms_2:
   push   r16
   ldi   r16,255
myWait_ms_1:
   dec   r16
   brne   myWait_ms_1
   pop   r16
   dec   r16
   brne   myWait_ms_2
   pop   r16
   dec   r16
   brne   myWait_ms_3
   pop   r16
   dec   r16
   brne   myWait_ms
   ret
;----------------------------------------------------------------------- 
--

Gruß Jahn
#353475
Lesenswert?

Man braucht dazu nicht extra nen Codewizard zu bemühen.

Der Assembler-Präprozessor kann doch selber 32Bit Berechnungen
durchführen.
Einfach bei XTAL die gewünschte Quarzfrequenz eintragen:
1
;************************************************************************/
2
;*                                                                     
3
*/
4
;*                      Delay Macro 8 ... 65543 Cycle                  
5
*/
6
;*                                                                     
7
*/
8
;*              Author: Peter Dannegger                                
9
*/
10
;*                      danni@specs.de                                 
11
*/
12
;*                                                                     
13
*/
14
;************************************************************************/
15
.listmac
16

17
;delay 8 ... 65543 cycle
18

19
.macro  mdelay
20
        ldi     r24, low( @0 - 8 )
21
        ldi     r25, high( @0 - 8 )
22
        sbiw    r24, 4
23
        brcc    pc - 1
24
        cpi     r24, 0xFD
25
        brcs    pc + 4
26
        breq    pc + 3
27
        cpi     r24, 0xFF
28
        breq    pc + 1
29
.endmacro
30

31
;------------------------------------------------------------------------
32
.equ    xtal = 16000000         ;16MHz
33

34
;delay 1 ... 255, 256 ms
35

36
;input: R16 = 1 ... 255, 0
37
;used:  R16, R24, R25
38

39
delay_n_ms:
40
        mdelay  (xtal + 500) / 1000     ;1ms
41
        dec     r16
42
        brne    delay_n_ms
43
        ret
44
;------------------------------------------------------------------------


Peter
Gast #353476
Lesenswert?

also, ich haääte vielleicht dazusagen müssen, das ich inzwischen nun
schon  das kapitel 1,2und 4 des avr tutorials auf dieser page durch
habe und gut verstanden habe. un das mit den takzyklen hört sich ganz
gut an.
ich habe den AT Mega 8, der hat 16 Mhz. Kann mir jemand ganz simple
erklären, was ein takt zyklus ist? Und was die Mhz aussgaen, je mehr
Mhz, desto mehr zyklen pro sekunde ??!?

danke!
#353477
Lesenswert?

Ein µProzessor hat einen Grundtakt. Der Grundtakt
ist so was wie der Mann der auf einer Galeere: Der
Schlägt auf eine Pauke und gibt so den Takt vor in
dem gerudert wird.

In deinem Prozessor wird zwar nicht gerudert, aber
verschiedene Einheiten müssen da zusammenarbeiten.
Wann welche Einheit was zu tun hat, gibt der Systemtakt
vor.

Dein Mega8 wird mit 16Mhz betrieben. Das heisst der Quarz
schwingt 16 Millionen mal in der Sekunde. Folgerichtig
'schlägt der kleine Japaner im Chip' 16 Millionen mal
in der Sekunde auf die Pauke und es passieren in einer
Sekunde 16 Millionen Arbeitsschritte.
Die Zeit von einem Schlag zum nächsten ist der Taktzyklus.
Viele (die meisten) Befehle können auf deinem Mega8 in
einem Taktzyklus abgearbeitet werden. D.h. dein Mega8
kann dann im Extremfall 16 Millionen Befehle in der
Sekunde abarbeiten.
Gast #353478
Lesenswert?

Da du 16MHz bebutzt, braucht dein Atmel für einen Arbeitstakt
1/16000000 Sekunden. (62,5 Nanosekunden)

Für die Pause von 1 ms mußt du also 16000 Takte "verbraten".

Wenn du eine simple Schleife nutzen willst, mußt du entweder eine
16bit
Variable benutzen oder eine innere und eine äußere Schleife verwenden.
Gast #353479
Lesenswert?

Ok, nu check ich das, danke für die gute beschreibung! Gibt es denn
einen einfachen befehl um eine bestimmte anzahl von zyklen "nichts"
zu tun?

Was sind denn "us" die bei der simulation unter prozessor/stopwatch
gezählt werden?
Gast #353483
Lesenswert?

Ich glaube, das mit den Zählschleifen hatten wir oben schon mal... Du
scheinst zu vergessen, dass Du in Assembler programmierst. Da ist halt
alles ein bisschen einfacher gehalten (ein Befehl macht genau eine
Elementar-Operation, in den meisten Fällen bezogen auf einen einzigen
Taktzyklus). Man muss dann eben ne Menge selber machen, lernt dadurch
aber die Hardware besser kennen, als wenn man gleich mit C (oder gar
Basic) anfängt. Musst Dir da halt was einfallen lassen.
#353484
Lesenswert?

Du kannst eine Schleife drum herum legen.
So nach dem Muster:
1  Register mit einem Wert laden
2  nichts tun
3  Register um 1 vermindern
4  0 erreicht?
5  wenn nein, gehe zu 2

Hinweis: In einem Register kannst du nur Zahlen
bis 256 laden. D.h. du kannst nicht in einem
Rutsch mit nur einem Register von 16000 bis
auf 0 herunter zählen.
Es hindert dich aber niemand daran immer wieder
von 255 bis 0 zu zählen und das 62 mal :-)
#353486
Lesenswert?

> ok, kann ich "nop" auch irgendwie multiplizieren? Ich mächte ja
> nicht 16000 mal nop schreiben

Für relativ kurze Wartezeiten (Zeitschleifen) nutze ich gern mal rcall
/ ret, das vertrödelt schonmal 7 Takte pro Aufruf.

Aber solch "lange Verzögerungen" realisiert man besser mittels Timer.
Dabei sollte man sich mit einem ständig laufenden Timer Interrups im
Zeitabstand von 0,1ms..20ms (je nach sonstigen Aufgaben des Timers)
erzeugen, in denen man den Rest per "Software-Timer" machen kann.

Den Timer-Int braucht man sowiso zum Entprellen von Tasten, Generieren
von Blinktakten und anderen immer wieder gebrauchten Dingen, da kann
man sich also schon von Anfang an mit beschäftigen.

Zuvor solltest du aber das Prinzip der Warteschleife verstanden haben.
Das sind ineinander verschachtelte Zählschleifen, die erst verlassen
werden, wenn die gewünschte (voreingestellte) Anzahl "Runden"
absolviert wurde. Da ein MC während einer Warteschleife nichts anderes
(sinnvolleres) machen kann als eben Wartezeit abzählen, sollte man
Warteschleifen nur für sehr kurze Verzögeringen nutzen und ansonsten
meiden.

...
Gast #353487
Lesenswert?

Eine einfache 16bit-Warteschleife sieht so aus:

FOO:
    subi r16, low(1)   ; 1
    sbci r17, high(1)  ; 1
    brne FOO           ; 2 bei Sprung, sonst 1
                       ; 4n - 1

Ich habe hinter jede Answeisung als Kommentar die Zahl an Taktzyklen
geschrieben, die sie braucht. Die Schleife braucht pro Durchlauf 4
Taktzyklen, man muß also die gewünschte Zahl an Taktzyklen durch 4
teilen (zwei Bits nach rechts schieben), um den Wert zu bekommen, mit
dem der Schleifenzähler initialisiert werden muß. Dann schreibt man die
unteren 8 bit nach r16, die oberen nach r17.
#353488
Lesenswert?

Nagut, dann will ich auch mal ein Warteschleifen-Beispiel posten. Es
diente zum Erzeugen der Wartezeiten beim Initialisieren eines LCDs. Zu
diesem Zeitpunkt (während der Reset-Routine) ist der Timer noch nicht
aktiviert, deshalb diese Warteschleifen.

;====================================================================
; Interne Routinen, werden nur von lcd_init und lcd_clear aufgerufen
;--------------------------------------------------------------------
wait1ms:            ;wartet etwa 1 Millisekunden
 push wl                    ;Variablen beschaffen
 push wh
 ldi wl,low(clock/1000/25)  ;Startwert setzen
 ldi wh,high(clock/1000/25) ;(AVR-Takt/1000Hz/25 Takte je Runde)
 rjmp wait1                 ;weiter...

wait5ms:            ;wartet etwa 5 Millisekunden
 push wl                    ;Variablen beschaffen
 push wh
 ldi wl,low(clock/200/25)   ;Startwert setzen
 ldi wh,high(clock/200/25)  ;(AVR-Takt/200Hz/25 Takte je Runde)
 rjmp wait1                 ;weiter...

wait10ms:           ;wartet etwa 10 Millisekunden
 push wl                    ;Variablen beschaffen
 push wh
 ldi wl,low(clock/100/25)   ;Startwert setzen
 ldi wh,high(clock/100/25)  ;(AVR-Takt/100Hz/25 Takte je Runde)
wait1:
 rcall waitend              ;7 Takte trödeln
 rcall waitend              ;7 Takte trödeln
 rcall waitend              ;7 Takte trödeln
 subi wl,1                  ;Low-Byte vermindern
 sbc wh,null                ;High-Byte um Übertrag (Carry) vermindern
 brcc wait1                 ;nochmal, solange $0000 -> $ffff kein
                            ;Carry setzt
 pop wh                     ;Variablen
 pop wl                     ;entsorgen
waitend:
 ret                        ;fertig bzw.zurück...

Die Register "wl" und "wh" sind meist r24 und r25, das Register
"null" ist meist r3 und enthält immer 0. Die Konstante "clock"
enthält die Taktfrequenz des AVRs. Die veränderten Register werden
gesichert und wiederhergestellt.

...
Gast #353489
Lesenswert?

So, wenn du keinen Timer benutzen möchtest:
Wenn's nicht genau eine ms ist, NOPs einfügen bzw, Zählerwerte
verringern

pause:
 ldi counter1,0xFF ;innerer Zähler (255)
 ldi counter2,0x3E ;äußerer Zähler (62)

 pause_loop_aussen:
  dec counter2

   pause_loop_innen:
    dec counter1
   brne pause_loop_innen

  brne pause_loop_aussen



ret
Gast #353490
Lesenswert?

Wegen der Taktberechnung ist dass nicht immer genau hinzubekommen, da
einige Befehle auch 2 oder 3 Takte benötigen. Die Rechnerei ist sehr
mühsam, da Sprungbefehle in Abhängigkeit der Bedingung unterschiedliche
Taktanzahlen benötigen. Ich lasse mir meine Warteroutinen vom CodeWizard
im myAVR Workpad genau auf den Wert machen den ich brauche. Hie eine
ziehmlich genaue 1ms Routine (gerechnet incl. Aufruf).

tori

;--------------------------------------------------------------
; Titel : Warteroutinen Beispiel
;--------------------------------------------------------------
; Funktion : WarteRoutinen
; Schaltung :
;--------------------------------------------------------------
; Prozessor : ATmega8
; Takt : 16000000 Hz
; Sprache : Assembler
; Datum : 19.7.2006
; Version : 1.0
; Autor : tori
; Programmer:
; Port :
;--------------------------------------------------------------
; created by myAVR-CodeWizard
;--------------------------------------------------------------
.include "avr.h"

...

;--------------------------------------------------------------------
; Warte-Routine für 1 ms
; die Routine wartet inclusive Aufruf 1,014 ms
;--------------------------------------------------------------------
myWait_1ms:
   push   r16
   ldi   r16,1
myWait_1ms_3:
   push   r16
   ldi   r16,21
myWait_1ms_2:
   push   r16
   ldi   r16,255
myWait_1ms_1:
   dec   r16
   brne   myWait_1ms_1
   pop   r16
   dec   r16
   brne   myWait_1ms_2
   pop   r16
   dec   r16
   brne   myWait_1ms_3
   pop   r16
   ret
#353491
Lesenswert?

@tori,

"Wegen der Taktberechnung ist dass nicht immer genau hinzubekommen,
da
einige Befehle auch 2 oder 3 Takte benötigen."


Das kann man aber auch ausnutzen.

Schau Dir mal mein obiges Macro an. Dem kann man jeden Wert von 8 ...
65543 Zyklen übergeben und die werden haargenau eingehalten.


Peter


P.S.:
Habe ich schon gesagt, daß Delayschleifen ohne Timer nur dann stimmen
können, wenn absolut kein einziger Interrupt verwendet wird.
#353492
Lesenswert?

@Tori:
Wer sich von irgendwelchen Codewizzards oder Berechnungsprogrammen
abhängig macht, verlernt im allgemeinen das logische Denken. Dass ein
Verzweigungsbefehl mal mehr und mal weniger Takte braucht, ist
überhaupt kein Problem, denn man sieht ja, welcher Fall eintritt.

...
#353494
Lesenswert?

Normalerweise würde man sagen:
Hol dir von Atmel das 'Instruction Set'

Da sind zumindest alle Befehle drinn, die der
Atmel kennt.

Nur ist das halt nur die halbe Miete. Die Befehle
alleine machens nicht. Erst die Kombination
der Befehle bringt Leben in die Bude.

Schau dich mal hier um
http://www.avr-asm-tutorial.net/avr_en/index.html

Da gibts einige Tutorials die dir die Grundlagen
beibringen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren