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
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)
> 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.
... .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
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
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
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
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!
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.
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.
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?
Der Befehl heißt nop und wartet genau einen Taktzyklus. Die Stopwatch zählt µs (Mikrosekunden)
ok, kann ich "nop" auch irgendwie multiplizieren? Ich mächte ja nicht 16000 mal nop schreiben
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.
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 :-)
Er kann aber mit adiw bzw. sbiw mit 16-Bit-Werten arbeiten, muss dann allerdings die richtigen Register dafür nehmen...
> 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. ...
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.
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. ...
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
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
@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.
@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. ...
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?
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.