Forum: Mikrocontroller und Digitale Elektronik AVR Studio 4, Assembler und Timer/Watchdog


von Stephan E. (loetzinn02)


Lesenswert?

Hallo in die geschätzte Expertenrunde,

vielleicht kann mir jemand auf die Sprünge helfen. Derzeit simuliere ich 
einen ATTiny13A (default-Fuses) im AVR Studio 4.16.628 unter Linux mit 
Wine.
Dabei ist mir eine Anomalie aufgefallen, für die ich keine Erklärung 
habe.

Anbei voneinander nur leicht abweichende Assembler-Codes (die vielen 
Leerzeilen werden hier scheinbar vom Forums-Editor eingefügt, weiß nicht 
wo die herkommen. Im Original sind die nicht vorhanden).

Der eine nutzt drei Interrupts: einmal beim Match von OC0B im Timer0, 
dann beim Überlauf des Timers und als dritten den Watchdog in der 
Interrupt-only-Variante. Das funktioniert erst mal wie erwartet. Der 
OC0B-Match-Interrupt wird ausgelöst, danach der erste Timer-Überlauf. 
Der passende Interrupt wird auch hier ausgelöst und abgearbeitet - dann 
bleibt der Timer0 einfach auf Null stehen und das Programm verbleibt in 
der Endlosschleife, der Watchdog springt niemals an.

Darunter eine leicht abgewandelte Programmversion, die auf dem Vergleich 
OC0B verzichtet, alles andere ist gleich. Hier ein ähnliches Verhalten: 
Der erste Interrupt beim Timerüberlauf wird ausgelöst und abgearbeitet - 
der Timer bleibt auch jetzt bei Null stehen und läuft nicht mehr.
Allerdings funktioniert im Gegensatz zur ersten Variante jetzt der 
Watchdog, der wie erwartet etwa alle 32 simulierten Millisekunden 
auslöst.

Hat zufällig jemand eine Erklärung?


Variante A: Drei Interrupts
1
.nolist
2
.include "tn13adef.inc" ; Define device ATtiny13A
3
.list
4
.def sreg_bak = R15 
5
.def rmp = R16 
6
.dseg
7
.org SRAM_START
8
.cseg
9
.org 0x0000
10
rjmp Main ; Reset vector
11
reti ; INT0
12
reti ; PCI0
13
rcall isr_timer_overflow ; OVF0
14
reti ; ERDY
15
reti ; ACI
16
reti; OC0A
17
rcall isr_oc0b; OC0B
18
rcall isr_watchdog ; WDT
19
reti ; ADCC
20
21
init_watchdog:
22
 wdr
23
 in rmp, MCUSR
24
 andi rmp, (0xff - (1<<WDRF))
25
 out MCUSR, rmp
26
 in rmp, WDTCR
27
 ori rmp, (1<<WDTIE)|(1<<WDP0) ; Interrupt-only und prescaler 32ms
28
 out WDTCR, rmp
29
 ret
30
31
init_timer0:
32
 push rmp
33
 ldi rmp, 220
34
 out OCR0B, rmp
35
 ldi rmp,(1<<WGM01)
36
 out TCCR0A, rmp
37
 ldi rmp, (1<<CS01)
38
 out TCCR0B, rmp
39
 ldi rmp, (1<<TOIE0)|(1<<OCIE0B)
40
 out TIMSK0, rmp
41
 pop rmp
42
 ret
43
44
45
Main:
46
 ldi rmp,Low(RAMEND)
47
 out SPL,rmp 
48
 rcall init_watchdog
49
 rcall init_timer0
50
 sbi DDRB,1
51
 sei 
52
53
Loop:
54
 nop
55
 rjmp Loop
56
57
58
isr_timer_overflow:
59
 in sreg_bak, SREG
60
 cbi PORTB,1
61
 out SREG, sreg_bak
62
 reti
63
64
isr_oc0b:
65
 reti




Variante B: Zwei Interrupts
1
.nolist
2
.include "tn13adef.inc"
3
.list
4
.def sreg_bak = R15
5
.def rmp = R16
6
.dseg
7
.org SRAM_START
8
.cseg
9
.org 0x0000
10
11
rjmp Main 
12
reti ; INT0
13
reti ; PCI0
14
rcall isr_timer_overflow ; OVF0
15
reti ; ERDY
16
reti ; ACI
17
reti; isr_oc0a; OC0A
18
reti; rcall isr_oc0b; OC0B
19
rcall isr_watchdog ; WDT
20
reti ; ADCC
21
22
init_watchdog:
23
 wdr
24
 in rmp, MCUSR
25
 andi rmp, (0xff - (1<<WDRF))
26
 out MCUSR, rmp
27
 in rmp, WDTCR
28
 ori rmp, (1<<WDTIE)|(1<<WDP0) ; prescaler 32ms
29
 out WDTCR, rmp
30
 ret
31
32
init_timer0:
33
 push rmp
34
 ldi rmp, 220
35
 out OCR0B, rmp
36
 ldi rmp,(1<<WGM01)
37
 out TCCR0A, rmp
38
 ldi rmp, (1<<CS01)
39
 out TCCR0B, rmp
40
 ldi rmp, (1<<TOIE0)
41
 out TIMSK0, rmp
42
 pop rmp
43
 ret
44
45
46
Main:
47
 ldi rmp,Low(RAMEND)
48
 out SPL,rmp
49
 rcall init_watchdog
50
 rcall init_timer0
51
 sbi DDRB,1
52
 sei 
53
54
Loop:
55
 nop
56
 rjmp Loop
57
58
59
isr_timer_overflow:
60
 in sreg_bak, SREG
61
 cbi PORTB,1
62
 out SREG, sreg_bak
63
 reti
64
65
isr_watchdog:
66
 in sreg_bak, SREG
67
 sbi PORTB,1
68
 out SREG, sreg_bak
69
 reti

von Wastl (hartundweichware)


Angehängte Dateien:

Lesenswert?

Stephan E. schrieb:
> Hallo in die geschätzte Expertenrunde

Fuck! Kannst du lesen und das freundlicherweise befolgen?

"Längerer Sourcecode" ist wenn es mehr als eine Seite ist.

von S. L. (sldt)


Lesenswert?

Ganz auf die Schnelle:
1
...
2
reti; OC0A
3
rcall isr_oc0b; OC0B
4
rcall isr_watchdog ; WDT
5
reti ; ADCC
mutet seltsam an, soll das nicht 'rjmp' heißen?

von Stephan E. (loetzinn02)


Angehängte Dateien:

Lesenswert?

Wastl schrieb:
> Fuck! Kannst du lesen und das freundlicherweise befolgen?

Sorry, habe ich wohl übersehen. Und so lange ist's ja auch wieder nicht.

von Stephan E. (loetzinn02)


Lesenswert?

S. L. schrieb:
> rcall isr_watchdog ; WDT
> reti ; ADCC
> mutet seltsam an, soll das nicht 'rjmp' heißen?

Habe ich mal getestet, scheint beides zu funktionieren. Aber nachdem 
auch im Tutorial überall rjmp verwendet wird, werde ich das künftig auch 
tun. Danke für den Hinweis.
Ansonsten hat sich leider nichts geändert - der Timer0 bleibt nach dem 
ersten Überlauf einfach stehen.

von S. L. (sldt)


Lesenswert?

Wieder auf die Schnelle:

> scheint beides zu funktionieren

Kann ich mir nicht vorstellen - in der 1. Version zum Beispiel wird ja 
grundsätzlich nach isr_oc0b auch noch isr_watchdog abgearbeitet.

Ansonsten: Simulator & Interrupts - ob das immer funktioniert? Da gab es 
mal Beiträge ...
  Und warum nicht der ATtiny85 - den könnte ich hier gegebenenfalls 
realiter laufen lassen.

von C-hater (c-hater)


Lesenswert?

Stephan E. schrieb:

> Habe ich mal getestet, scheint beides zu funktionieren.

Nicht wirklich. Bei jedem Interrupt werden zwei Byte auf dem Stack 
"verbraucht". Ist nur eine Frage der Zahl der Interrupts, bis der Stack 
überlauft (bzw. eigentlich: unterläuft). Das sinnlose (und am Ende 
tödliche) Wachsen des Stack sollte sich im Simulator wirklich sehr gut 
verfolgen lassen.

> Ansonsten hat sich leider nichts geändert - der Timer0 bleibt nach dem
> ersten Überlauf einfach stehen.

Das kann nicht sein. Dem Timer ist der Stack egal, dem Timer ist 
überhaupt egal, was die MCU tut. Einmal gestartet, läuft der einfach. Es 
sei denn, die MCU hält ihn an. Dafür gibt es aber keinen Code.

Und übrigens: in deiner Variante A gibt es darüber hinaus auch keinen 
Code für isr_watchdog. Das dürfte also nichtmal assemblierbar sein...

von Falk B. (falk)


Lesenswert?

S. L. schrieb:
> ...
> reti; OC0A
> rcall isr_oc0b; OC0B
> rcall isr_watchdog ; WDT
> reti ; ADCC
> mutet seltsam an, soll das nicht 'rjmp' heißen?

In der Tat.

von S. L. (sldt)


Lesenswert?

> Wachsen des Stack

?
Zurückgekehrt wird doch immer, oder? Nur an irrwitzigen Stellen.

von C-hater (c-hater)


Lesenswert?

S. L. schrieb:

> Ansonsten: Simulator & Interrupts - ob das immer funktioniert?

Jedenfalls bezüglich der MCU habe ich beim SimulatorV2 noch nie 
Gegenteiliges feststellen können.

Bugs gibt es aber reichlich in der Simulation der Peripherie und auch 
Peripherie, die praktisch garnicht simuliert wird. Bei letzterem kann 
man natürlich ewig auf das Auftreten der entsprechende Interrupts 
warten. Es werden keine erfolgen...

Wie auch immer, das Entscheidende scheint mir zu sein, dass angeblich 
der Timer stoppt. Das tut er natürlich nicht. Weder in der Simulation 
noch in der Realität. Ich tippe mal auf einen Bedienfehler. 
Möglicherweise ausgelöst durch einen Anzeigefehler, der durch Wine 
verursacht wird.
Ich kann mich erinnern, dass ich auch mal versucht habe, das Studio4 
unter Wine zu betreiben, das dann aber wegen extremer Anzeigefehler 
schon im Editor  schnell wieder aufgegeben habe. Bis zum Einsatz des 
Simulators bin ich also unter Wine niemals gekommen.

von C-hater (c-hater)


Lesenswert?

S. L. schrieb:
>> Wachsen des Stack
>
> ?
> Zurückgekehrt wird doch immer, oder?

Nicht im Simulator. Der kann Stack-Unterlauf diagnostizieren und hält 
dann an. Gibt aber natürlich dann auch eine ensprechende Fehlermeldung 
auf der Debugger-Console aus.

von S. L. (sldt)


Lesenswert?

Nein, das meinte ich nicht, sondern: es gibt hier keinen Stack-Unterlauf 
- wo sollte der auch auftreten?

von C-hater (c-hater)


Lesenswert?

S. L. schrieb:
> Nein, das meinte ich nicht, sondern: es gibt hier keinen Stack-Unterlauf
> - wo sollte der auch auftreten?

Es passiert folgendes:

IRQ:
- SREG_I wird gelöscht
- die Rücksprungadresse wird auf den Stack gelegt
- der Vektor wird angesprungen

Vektor:
- rcall legt legt erneut eine Rücksprungadresse auf den Stack
- springt das angegebene Ziel an

Ziel (ISR):
-wird abgearbeitet
-endet mit reti (setzt SREG_I, holt 1x Rücksprungadresse vom Stack)
-das ist die vom (falschen) rcall gesetzte Rücksprungadresse
-führt effektiv dazu, das der nächste Vektor in der Reihenfolge der
 Vektortabelle durchlaufen wird.

Und jetzt wird es spannend. Beim Overflow steht auf dem nächsten Vektor 
ein reti. Das holt die zweite Rücksprungadresse vom Stack (setzt das in 
diesem Fall bereits gesetzte SREG_I) und springt zur Rücksprungadresse. 
Alles gut hier, die Bilanz stimmt. Schwachsinn ist es trotzdem.

Aber, nun spiele mal selber durch, was bei OC0B passiert...

von S. L. (sldt)


Lesenswert?

> Aber, nun spiele mal selber durch, was bei OC0B passiert...

Das, was ich bereits geschrieben hatte:

> in der 1. Version zum Beispiel wird ja grundsätzlich
> nach isr_oc0b auch noch isr_watchdog abgearbeitet.

Und anschließend wird bei 'reti ; ADCC' ins Hauptprogramm zurückgekehrt 
- also ich sehe nach wie vor keinen Stack-Unterlauf.

von C-hater (c-hater)


Lesenswert?

S. L. schrieb:
>> Aber, nun spiele mal selber durch, was bei OC0B passiert...
>
> Das, was ich bereits geschrieben hatte:
>
>> in der 1. Version zum Beispiel wird ja grundsätzlich
>> nach isr_oc0b auch noch isr_watchdog abgearbeitet.

Wie kann das sein? isr_watchdog existiert in der "ersten Version" (vom 
TO als Version A bezeichnet) doch garnicht.

Was also steht auf diesem Vektor wirklich?

von S. L. (sldt)


Lesenswert?

> isr_watchdog existiert in der "ersten Version" (vom
> TO als Version A bezeichnet) doch garnicht.

In der "Monitor-Version" nicht, das stimmt, aber in der "Anhang-Version" 
- war wohl ein copy&paste-Versehen von Stephan E.

von S. L. (sldt)


Lesenswert?

an Stephan E.:

Zwei Anmerkungen (falls da Unkenntnis bestehen sollte):
 - 'reti' wird zwar mit "Return from interrupt" beschrieben, ist aber 
lediglich ein 'ret', welches zusätzlich das 'Global Interrupt Flag' in 
SREG setzt.
  - größere Programmblöcke können so auskommentiert werden:
1
/*
2
dies wird vorlaeufig stillgelegt:
3
disable_overflow:
4
 push rmp
5
 in rmp, TIMSK0
6
 andi rmp, (0<<TOIE0)
7
 out TIMSK0, rmp
8
 pop rmp
9
 ret
10
*/

von Falk B. (falk)


Lesenswert?

Vielleicht der falsche Controller im Simulator eingestellt? Denn der 
wird nicht automatisch aus dem Editor übernommen.

von Falk B. (falk)


Lesenswert?

Ich habs gerade mal probiert, der Timer bleibt nach dem ersten 
ISR-Aufruf im Simulator stehen!

von C-hater (c-hater)


Lesenswert?

Falk B. schrieb:
> Ich habs gerade mal probiert, der Timer bleibt nach dem ersten
> ISR-Aufruf im Simulator stehen!

Welcher Simulator? V1 oder V2?

von Falk B. (falk)


Lesenswert?

V2

von Falk B. (falk)


Lesenswert?

Mit V1 gehts auch nicht.  Und das Interrupt-Flag wird verspätet gesetzt, 
zumindest in der Anzeige der IO Register, da läuft die ISR schon ;-)
Aber auch bei Prescaler 1 bleibt der Timer stehen. Ein Bug im 
AVR-Studio.

von C-hater (c-hater)


Lesenswert?

Falk B. schrieb:

> Mit V1 gehts auch nicht.  Und das Interrupt-Flag wird verspätet gesetzt,
> zumindest in der Anzeige der IO Register, da läuft die ISR schon ;-)
> Aber auch bei Prescaler 1 bleibt der Timer stehen. Ein Bug im
> AVR-Studio.

Der V1-Simulator ist ziemlicher Rotz. Der bildet nichtmal die MCU 
vollständig korrekt ab. Der V2 hingegen tut wenigstens dies, hat aber 
auch immer noch ziemliche Mankos bezüglich der Peripherie. Für einen 
Tiny13 sollte es aber eigentlich reichen, der hat ja nun nicht sehr viel 
an Peripherie und im konkreten Fall ist nur Timer0 und Watchdog 
involviert.

Aber klar: Ein Bug im Simulator wäre definitiv möglich. Kann man am 
einfachsten dadurch umgehen, dass man halt echte Hardware verwendet...

von Stephan E. (loetzinn02)


Lesenswert?

Zunächst mal vielen Dank für die Antworten.
Was ich daraus mitgenommen habe:
- ISRs werden ab sofort nur noch per rjmp aufgerufen, nicht mehr per 
rcall.
- Mehrzeilige Kommentare wie in z.B. c++ oder JavaScript möglich (wußte 
ich bei assembler tatsächlich noch nicht)
- Der Timer-stop könnte tatsächlich ein Bug sein - ein extra Dank an die 
Tester. Ggf. kann ich das ja mit einem extra Timer-init im Interrupt im 
Quellcode "umgehen", mal schau'n.

von C-hater (c-hater)


Lesenswert?

Stephan E. schrieb:

> - Mehrzeilige Kommentare wie in z.B. c++ oder JavaScript möglich (wußte
> ich bei assembler tatsächlich noch nicht)

Der Avr-Asembler stellt (neben den C-Style-Kommentaren) auch eine 
C-Style Präprozessor zur Verfügung. Das erleichtert auch manches...

von Peter D. (peda)


Lesenswert?

Stephan E. schrieb:
> Habe ich mal getestet, scheint beides zu funktionieren.

Als Assemblerprogrammierer sollte man schon die Bedeutung der einzelnen 
Befehle gelesen und verstanden haben. Sonst funktionieren Deine 
Programme nur zufällig bzw. meistens nicht.
Zwischen JMP und CALL besteht ein gewaltiger Unterschied.

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.