Forum: Mikrocontroller und Digitale Elektronik Pause in AVR-Studio mit variable?


von Lukas H (Gast)


Lesenswert?

Hi, wie kann ich ne pause in AVR studio machen, mit einer variable?

mann kann ja einfach "delay50ms:" schreiben, ich möchte aber, das die
pause so viele millisekunden lang ist, wie es in z.B. r16 steht.

help please! thx

von Walter (Gast)


Lesenswert?

ASM oder C ?

Timer für 1ms (Vorwert berechnen)einrichten,
x-mal Überlauf = x-mal ms. (im Timeroverflowimterrupt r16
decrementieren)
Wenn r16 == 0, Dann Timer stoppen. (Prescaller off)

von johnny.m (Gast)


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.

von Lukas H (Gast)


Lesenswert?

mit asm

von Walter (Gast)


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

von Jahn (Gast)


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

von Jahn (Gast)


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

von Peter D. (peda)


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

von Lukas H (Gast)


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!

von Karl H. (kbuchegg)


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.

von Fritze (Gast)


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.

von Lukas (Gast)


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?

von johnny.m (Gast)


Lesenswert?

Der Befehl heißt nop und wartet genau einen Taktzyklus. Die Stopwatch
zählt µs (Mikrosekunden)

von Fritze (Gast)


Lesenswert?

- Ja,
  NOP, tut nichts für einen Takt

- us = µs
  Mikrosekunden

von Lukas (Gast)


Lesenswert?

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

von johnny.m (Gast)


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.

von Karl heinz B. (kbucheg)


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 :-)

von johnny.m (Gast)


Lesenswert?

Er kann aber mit adiw bzw. sbiw mit 16-Bit-Werten arbeiten, muss dann
allerdings die richtigen Register dafür nehmen...

von Hannes L. (hannes)


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.

...

von Rolf Magnus (Gast)


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.

von Hannes L. (hannes)


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.

...

von Fritze (Gast)


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

von tori (Gast)


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

von Peter D. (peda)


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.

von Hannes L. (hannes)


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.

...

von Lukas (Gast)


Lesenswert?

ok, dann feheln mir aber drei befehel, wie vermindere ich einen
register? wie prüfe ich ob in einem register eine bestimmte zahl drin
steht?

von Karl heinz B. (kbucheg)


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.

von Karl heinz B. (kbucheg)


Lesenswert?

Alternativ kannst du auch:

Von Atmel das 'Instruction Set Manual' downloaden
(Merkst du was? Ohne dieses Dokument kommst du nicht weit)

und dann einfach mal die bisher geposteten Lösungen
anylsieren. Alle Befehle die du nicht kennst schlägst
du im ISM nach.

von Hannes L. (hannes)


Lesenswert?

Das AVR-Studio hat auch eine Online-Hilfe. Wenn man den Editorcursor auf
einen ASM-Befehl setzt und dann die F1-Taste drückt, dann erfährt man
etwas über den betreffenden Befehl. Ist fast wie beim guten alten
QBASIC...

...

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.