Forum: Mikrocontroller und Digitale Elektronik Warteschleife für Atmega32 in Assembler


von A. B. (developer_x)


Lesenswert?

Sehr geehrtes Forum,
ich habe einen Atmega32, an den extern ein 20 MhZ Quarz
angeschlossen ist. Ich habe die Fuses auf
1
H:0x99
2
L:0xDF
gestellt.

Nun habe ich versucht ein Warteschleifenprogramm mit
Assembler zu schreiben, sodass eine LED jede Sekunde an bzw.
ausgehen soll, aber egal was ich auch versuche, ich bekomm es einfach 
nicht hin. Kann mir jemand helfen und mir einen Code mit einer 
Warteroutine von 1s
geben, bzw. sagen was mit meinem nicht stimmt und diesen somit 
modifizieren?

Ich will nämlich wissen ob mein MC wirklich 20MHz hat oder nicht, er 
muss auch sehr präzise laufen, deshalb will ich diesen Test machen.
1
.include "m32def.inc"         ; Definitionsdatei für den Prozessortyp einbinden
2
3
.equ c1 = 49 ; Anzahl Durchläufe der Schleife
4
5
; 20.000.000 Hz 
6
; 1 Takt = 0.00000005
7
ldi r16, 0xFF       ; lade Arbeitsregister r16 mit der Konstanten 0xFF
8
out DDRB, r16       ; Inhalt von r16 ins IO-Register DDRB ausgeben
9
 
10
.def temp = r16
11
LDI  R16, low(RAMEND)
12
OUT  SPL, R16
13
LDI  R16, high(RAMEND)
14
OUT  SPH, R16
15
16
loop:  
17
  ldi r17, 0b00000001 ; 0b11111100 in r16 laden
18
  out PORTB, r17      ; r16 ins IO-Register PORTB ausgeben
19
  rcall wait
20
21
  ldi r17, 0b00000000 ; 0b11111100 in r16 laden
22
  out PORTB, r17      ; r16 ins IO-Register PORTB ausgeben
23
  rcall wait
24
  
25
rjmp loop
26
27
28
wait:
29
  rcall wait3
30
  rcall wait3
31
  rcall wait3
32
  rcall wait3
33
  rcall wait3
34
  rcall wait3
35
  rcall wait3
36
  rcall wait3
37
  rcall wait3
38
  rcall wait3
39
ret
40
41
wait1:
42
  ldi R18,c1 ; Lade Register mit Schleifenwert
43
  Loops: ; Schleifenbeginn
44
  dec R18 ; Registerwert um Eins verringern
45
  brne Loops ; wenn nicht Null dann Schleifenbeginn
46
ret
47
48
wait2:
49
  ldi R19,c1 ; Lade Register mit Schleifenwert
50
  Loopss: ; Schleifenbeginn
51
  rcall wait1
52
  dec R19 ; Registerwert um Eins verringern
53
  brne Loopss ; wenn nicht Null dann Schleifenbeginn
54
ret
55
56
wait3:
57
  ldi R20,c1 ; Lade Register mit Schleifenwert
58
  Loopsss: ; Schleifenbeginn
59
  rcall wait2
60
  dec R20 ; Registerwert um Eins verringern
61
  brne Loopsss ; wenn nicht Null dann Schleifenbeginn
62
ret

Ich bekomm es einfach nicht gebacken das Programm im AVR Studio zu 
simulieren, ob nach einem wait aufruf tatsächlich 20.000.000 Takte 
vergangen sind, da ja die Simulation sonst zu lange dauern würde, die 
LED blinkt aber nicht gerade im Sekundentakt.

Kann mir da vielleicht jemand einen Code geben bzw. diesen hier 
verbessern`?

Danke,
m.f.G.: Developer_X

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

K. R. schrieb:
> Ich bekomm es einfach nicht gebacken das Programm im AVR Studio zu
> simulieren, ob nach einem wait aufruf tatsächlich 20.000.000 Takte
> vergangen sind, da ja die Simulation sonst zu lange dauern würde

dann simuliere doch einfach nur den aufruf

rcall wait3


dann sollte doch machbar sein, dann kannst du diesen wert mal 10 
rechnen.

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


Lesenswert?

Oder du setzt auf die Portausgabe einen Breakpoint, merkst dir den Cycle 
Counter und startest mit 'run'. Nach dem Kaffeetrinken wird der 
Breakpoint irgendwann erreicht sein und du kannst die Cycles ablesen.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

K. R. schrieb:
> Ich bekomm es einfach nicht gebacken das Programm im AVR Studio zu
> simulieren, ob nach einem wait aufruf tatsächlich 20.000.000 Takte
> vergangen sind, da ja die Simulation sonst zu lange dauern würde, die
> LED blinkt aber nicht gerade im Sekundentakt.

 Wie zu lange ?
 Ich glaube AVR Studio ist 8-10 mal langsamer.
 Wenn dir aber 8 Sec zu lang sind...

von Pink S. (pinkshell)


Lesenswert?

Nach meinen überchlägigen Rechnungen wird die Wartezeit nicht 1 Sekunde 
sein.

Ein Loop in wait1 = 4 Zyklen
Wait1 komplett = 4 * 50 = 200 Zyklen.
Wait2 komplett = 200 * 50 = 10000 Zyklen.
Wait3 komplett = 10k * 50 = 500k Zyklen.
10 mal wait3 = 10 * 500k Zyklen = 5 Mio. Zyklen = 0,25 Sekunden @ 20MHz

Dazu der Schleifen Overhead, ein paar Prozent dazu. Von präzise kann 
keine Rede sin.

von A. B. (developer_x)


Lesenswert?

So, ich habe das jetzt so eingestellt, bis wait3 genau 100.000 Zyklen 
dauert, jetzt funktioniert alles.

Das mit den 20 MHz stimmt, war mein erstes mal dass ich nen MC mit nem 
Quarz verbunden habe, danke :)

m.f.G.: DX

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

In deinem Code hat der Wait genau 3774547 Cycles gebraucht.
 Und es hat weniger als 4 Sec in AVR Studio gedauert...

: Bearbeitet durch User
von Blub (Gast)


Lesenswert?

Wer bitte schön programmiert eine Warteschleife? Mach das mit Interrupt. 
Gewöhn dir so neun Kram gar nicht erst an, auch nicht nur zum Test.

von Thomas (kosmos)


Lesenswert?

ich denke ich erkläre es so am schnellsten

a=0
b=0
c=0
Schleife1:
a=a-1
if a<>0 then goto Schleife1

Schleife2:
b=b-1
if b<>0 then goto Schleife1

Schleife3:
c=c-1
if c<>0 then goto Schleife1

so wird das ganze verschachtelt. Schleife1 läuft ja 255 mal durch x 255 
mal Schleife2 und dann nochmal 255 mal die Dritte Schleife.

Es gibt im Netzt eine Seite da gibts du die gewünschte Verzögerung ein 
und das Programm spuckt dir ein kleines Assemblerlisting aus.

Oder aber man nimmt einen Teiler mit passendem Teiler stellt ihn dann so 
ein das er nach einer gewünschten Zeit überläuft und einen Interrupt 
aufruft. Oder mittels Comparerregister. Dann kann man nämlich 
weiterarbeiten und hängt nicht in der Warteschleife fest.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Oder man schreibt ein kleines C-Programm:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
int main(void) {
5
  DDRB = 0xff;
6
  for(;;) {
7
    PORTB ^= 0x01;
8
    _delay_ms(1000);
9
  }
10
}

Beim Kompilieren muss man die Option -DF_CPU=20000000 mitgeben. Die
ganze Rechenarbeit erledigt netterweise der Compiler.

von Moby A. (moby-project) Benutzerseite


Lesenswert?

Blub schrieb:
> Wer bitte schön programmiert eine Warteschleife? Mach das mit
> Interrupt.

Na das würd ich aber auch meinen! Profane Warteschleifen wie auch 
komplexe Statusmaschinerie realisiere ich standardmäßig so:
1) In jedem System ist ein allgemeiner Timerinterrupt installiert (läuft 
bei mir stets mit 200Hz), der für verschiedenste, regelmäßige Aufgaben 
im Hintergrund eingesetzt wird. Unter anderem für
2) Eine Anzahl benötigter 16-Bit Variablen wird mit jedem zweiten 
Timerinterrupt (also in Hundertstelsekunden) bis Null dekrementiert. 
Braucht ein (Haupt)Programmteil nun eine Warteschleife, lädt es eine 
solche Variable schlicht mit dem benötigten Wert. Das Programm macht nun 
andere Dinge und testet seine Variable zwischendurch auf Null... Soll 
bei Ablauf der Zeitspanne (bzw. einem Zwischenschritt) sofort etwas 
geschehen, kann man das natürlich auch gleich im Timerinterrupt machen.

von c-hater (Gast)


Lesenswert?

Yalu X. schrieb:

> Die
> ganze Rechenarbeit erledigt netterweise der Compiler.

Das geht natürlich auch in Asm. Man muß sich eben nur mal hinsetzen und 
den entsprechenden Code schreiben (ist eigentlich viel mehr 
Macroprogrammierung als Assemblercode).

von Yalu X. (yalu) (Moderator)


Lesenswert?

c-hater schrieb:
> Das geht natürlich auch in Asm.

Schon.

> Man muß sich eben nur mal hinsetzen und den entsprechenden Code
> schreiben

Eben.

Der TE wollte ja nur mal kurz prüfen, ob sein Controller mit externem
Quarz in der entsprechenden Konfiguration tatsächlich mit den erwarteten
20 MHz läuft. In solchen Fällen will man vor allem zwei Dinge:

  - minimalen Arbeitsaufwand

  - fehlerfreien Testcode

Das erreicht man am besten dadurch, dass man auf Code von anderen (hier
die _delay_ms-Funktion) zurückgreift, der bereits vielfach getestet und
einfach in der Handhabung ist.

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.