Forum: Mikrocontroller und Digitale Elektronik [Assembler] Simulation in AVRStudio geht, in anderem Programm nicht


von Sven J. (locutussum)


Lesenswert?

Hallo,

ich versuche gerade, ein kleines Programm für den attiny11 zu schreiben 
(hab von dem noch ein, zwei rumfliegen und das, was ich machen will ist 
nicht wirklich umfangreich!).
Ziel soll im Endeffekt ein Einlesen von Tastern und aufgrund von 
Tastendrücken ein Dekrementieren bzw. Inkrementieren von einem 
Ausgabewert auf 3 Pins (also im endeffekt BCD-Code mit 3 stellen als 
Ausgabe) sein.
Dazu dann natürlich noch eine Entprellung und ein "automatisch alle 1s 
eins weiter schalten"-funktion bei gedrücktem Knopf.

Fertig geschrieben sieht das ganze bei mir so aus:
1
.include "tn11def.inc"
2
.org 0x0000
3
  rjmp main                  ; Reset Handler
4
.org OVF0addr
5
  rjmp timer0_overflow       ; Timer Overflow Handler
6
7
8
timer0_overflow:
9
  sbrs r18, 0                 ; wenn Entprellhandler nicht gesetzt
10
  rjmp output                ; Eingänge überprüfen
11
  subi r19, 1                 ; Timercounter dekrementieren
12
  cpi r19, 0
13
  breq action                  ; und wenn auf 0
14
  rjmp loop
15
16
action: 
17
  ldi r18, 0               ; Entprellhandler ausschalten
18
  rjmp loop
19
20
output:
21
  ldi r18, 1                ; Entprellhandler setzen
22
  ldi r19, 10               ; 10 Interrupts bis nächste Ausführung Ausgabe
23
  sbis PINB, 3
24
  add r17, r20              ; Eingänge einlesen
25
  sbis PINB, 4
26
  sub r17, r20
27
  SBRC r17, 5               ; Wenn über "7"
28
  ldi r17, 0b00011000       ; zurück auf "0"
29
  SBRS r17, 3               ; Wenn unter "0"
30
  ldi r17, 0b00011111       ; zurück auf "7"
31
  out PORTB, r17            ; Ausgeben
32
  rjmp loop
33
 
34
main: 
35
  ldi r20, 1             ; eine  EINS
36
  ldi r19, 0                ; Timercounter
37
  ldi r18, 0                ; Entprellhandler
38
  ldi r17, 0b00011000      ; BCD-Basis-System
39
  ldi r16, 0b00000111      ; IO-Aufbau
40
  out DDRB, r16
41
  out PORTB, r17
42
  ldi r16, (1<<CS02)       ; CS02 setzen: Teiler 256
43
  out TCCR0, r16
44
  ldi r16, (1<<TOIE0)      ; TOIE0: Interrupt bei Timer Overflow
45
  out TIMSK, r16
46
  ldi r16, (1<<7)
47
  rjmp loop
48
49
loop:
50
    out SREG, r16
51
  rjmp loop           ; Warteschleife

(ich hoffe mal, die kommentare machen das einigermaßen verständlich)

jetzt mein Problem: wenn ich das ganze in AVR-Studio simuliere, dann 
läuft das erst mal ganz gut - bis auf den Fehler "HW Stack Overflow" 
oder so, welcher mir angezeigt wird, welchen ich aber auch nur so 
ausmerzen könnte, dass ich dabei gleichzeitig im SREG das interrupt-Flag 
nicht regelmäßig neu setzen würde. (und dann würde das ganze nicht mehr 
funktionieren, da sich das flag nach jedem interrupt löscht? o.O)
Im handbetrieb initialisiert sich der Timer nicht richtig und arbeitet 
auch sonst nur bedingt: eigentlich sollte der prescaler auf 256 stehen, 
lasse ich das ganze aber "laufen", so wird der Prescaler immer auf "kein 
teiler" gestellt. ist das ein Fehler im programm? oder absicht?
wenn ich im angehaltenen Zustand die Pins verändere, welche eben später 
fürs Input verantwortlich sind, dann funktioniert das im "laufen lassen" 
auch ganz gut, ich bekomme den bcd-code raus und der schaltet sich auch 
schön durch.

aber jetzt mein größeres Problem: da ich z.Z. keinen Programmer habe, 
welcher auch den tiny11 programmieren kann, kann ich das programm noch 
nicht in der Schaltung testen. wohl aber in Prospice simulieren. und bei 
der simulation scheint auch wieder einiges schiefzulaufen: nach dem 
starten und initialisieren kommt an den ausgängen überall "low" (soweit 
ja richtig) und die input-pins werden beide "high" (soweit ja auch 
richtig, sind ja die pullups!). wenn ich aber über eine Taste einen der 
Input-Pins auf "low" ziehe, so ändert sich an den ausgängen rein 
garnichts. und ich weis wirklich nicht wieso. (mit dem debugger in 
prospice konnte ich zumindenst feststellen, dass der timer vernünftig 
läuft und regelmäßig ein interrupt aufwirft! aber Aktionen am PortB hat 
er komischerweise überhaupt nicht, auch wenn ich an dem Port ne taste 
drücke!)

hat da wer ne idee, woran das liegt? hab ich da evtl. noch irgendetwas 
falsch gemacht in meinem code?

von Karl H. (kbuchegg)


Lesenswert?

1
timer0_overflow:
2
  sbrs r18, 0                 ; wenn Entprellhandler nicht gesetzt
3
  rjmp output                ; Eingänge überprüfen
4
  subi r19, 1                 ; Timercounter dekrementieren
5
  cpi r19, 0
6
  breq action                  ; und wenn auf 0
7
  rjmp loop

  rjmp loop  ?

Bist du des Wahnsinns fette Beute?
Aus einer ISR steigt man mit reti aus und sonst mit nichts anderem. Das 
ist konzeptionell ein Unterprogrammaufruf! So richtig mit: Returnadresse 
wird auf den Stack geschrieben und so.

Wenn man schon anders raus muss, dann korrigiert man zumindest den 
Stackpointer. Aber eigentlich sollte man solche Querausstiege nicht 
machen, eben weil man dabei gerne zuviele Dinge übersieht.

Dann kannst du auch das SREG in der Hauptschleife in Ruhe lassen.

von MWS (Gast)


Lesenswert?

Willst Du das als Schadcode bei den Borg einspielen ? :D

Schon mal was von RETI gehört ? Das ist ein völliger 
Durcheinandergespringe, kein Wunder, daß der Stack überläuft, das geht 
so grundsätzlich nicht.

von Spess53 (Gast)


Lesenswert?

Hi

>timer0_overflow:
>  sbrs r18, 0                 ; wenn Entprellhandler nicht gesetzt
....
>  rjmp loop

Was soll denn das? Eine Interruptroutine wird mit 'reti' beendet. Kein 
Wunder das du einen Stack-Overflow bekommst.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Selbiges nach
action:
und
output:

von Grrrr (Gast)


Lesenswert?

Sven J. schrieb:
> (ich hoffe mal, die kommentare machen das einigermaßen verständlich)

Leider nicht wirklich.

> welchen ich aber auch nur so
> ausmerzen könnte

Man kann auch den Stackpointer selbst initialisieren falls man nicht 
glauben will oder kann das eine höhere Macht das schon tun wird. :-)

von Spess53 (Gast)


Lesenswert?

Hi

>Man kann auch den Stackpointer selbst initialisieren falls man nicht
>glauben will oder kann das eine höhere Macht das schon tun wird. :-)

Der ATTiny11 hat einen Hardwarestack. Also keine Initialisierung nötig.

MfG Spess

von Grrrr (Gast)


Lesenswert?

Grrrr schrieb:
> Man kann auch den Stackpointer selbst initialisieren falls man nicht
> glauben will oder kann das eine höhere Macht das schon tun wird. :-)

Ach Mist. Ist ja ein Tiny11. Vergiss es.

von Grrrr (Gast)


Lesenswert?

Spess53 schrieb:
> Der ATTiny11 hat einen Hardwarestack. Also keine Initialisierung nötig.

Richtig.

von Sven J. (locutussum)


Lesenswert?

sorry, aber ist schon was her, dass ich das letzte mal was in assembler 
machen musste und ich hab inzwischen auch alle befehle weitestgehend 
vergessen (und die, die ich noch wusste, wie adi, die gehen nicht mal 
bei avr. :( ), so dass ich mir die befehle erst selbst wieder zusammen 
suchen musste.
ansonsten simulierts sich jetzt auch wundervoll in ISIS, wenn ich knopf 
drücke, zählt die bcd-anzeige schön hoch, anderer knopf zählt runter 
(und reset macht natürlich reset), sodass ich jetzt davon ausgehen kann, 
dass der code so wie er jetzt ist auch wirklich funktioniert. (muss wohl 
wirklich einzig und alleine am reti gelegen haben, wo mir der befehl 
doch so schön entfallen war. ^^) also danke an alle, die mich auf das 
RETI hingewiesen haben.

und wie sieht das mit dem I-Bit in SREG aus? kann ich das nicht irgendwo 
einfacher (und weniger ... ìdiotisch?) dazu zwingen, das flag gesetzt zu 
halten?

von Spess53 (Gast)


Lesenswert?

Hi

>und wie sieht das mit dem I-Bit in SREG aus? kann ich das nicht irgendwo
>einfacher (und weniger ... ìdiotisch?) dazu zwingen, das flag gesetzt zu
>halten?

Normalerweise wird das I-Flag mit sei gesetzt und mit cli gelöscht. Und 
von allein ändert es sich nicht.

MfG Spess

von Kluchscheißernder N. (kluchscheisser)


Lesenswert?

Spess53 schrieb:
> Hi
>
>>und wie sieht das mit dem I-Bit in SREG aus? kann ich das nicht irgendwo
>>einfacher (und weniger ... ìdiotisch?) dazu zwingen, das flag gesetzt zu
>>halten?
>
> Normalerweise wird das I-Flag mit sei gesetzt und mit cli gelöscht. Und
> von allein ändert es sich nicht.

Hmmm, ääähhhh, und was ist mit Interrupts?

Das I-Flag wird (beim AVR) beim Auslösen eines Interrupts per Hardware 
gelöscht und beim verlassen der ISR per RETI wieder gesetzt. Dies 
verhindert, dass während der Abarbeitung eines Interrupts ein weiterer 
Interrupt abgearbeitet wird.

Dies nur als Ergänzung, ansonsten kannst Du Spess53 schon vertrauen. ^^

>
> MfG Spess

MfG, Kluchscheißer

von Spess53 (Gast)


Lesenswert?

Hi

>Hmmm, ääähhhh, und was ist mit Interrupts?

OK. Kleine Unterlassungssünde. Aber das von Problem von Sven sollte sich 
mit dem richtigen Einsatz von 'reti' erledigt haben.

MfG Spess

von Sven J. (locutussum)


Lesenswert?

stimmt. das sei funktioniert einwandfrei! (ich hatte das vorher schon 
mal probiert, da hat es aber nicht so richtig funktioniert, wird wohl an 
den sprüngen gelegen haben!)

jetzt sieht der code so aus:
1
.include "tn11def.inc"
2
.org 0x0000
3
  rjmp main                  ; Reset Handler
4
.org OVF0addr
5
  rjmp timer0_overflow       ; Timer Overflow Handler
6
7
8
timer0_overflow:
9
  sbrs r18, 0                 ; wenn Entprellhandler nicht gesetzt
10
  rjmp readin                ; Eingänge überprüfen
11
  subi r19, 1                 ; Timercounter dekrementieren
12
  cpi r19, 0
13
  breq action                  ; und wenn auf 0
14
  reti
15
16
action: 
17
  ldi r18, 0               ; Entprellhandler ausschalten
18
  reti
19
20
readin:
21
  mov r21, r17
22
  ldi r19, 10               ; 10 Interrupts bis nächste Ausführung Ausgabe
23
  sbis PINB, 3
24
  add r17, r20              ; Eingänge einlesen
25
  sbis PINB, 4
26
  sub r17, r20
27
  SBRC r17, 5               ; Wenn über "7"
28
  ldi r17, 0b00011000       ; zurück auf "0"
29
  SBRS r17, 3               ; Wenn unter "0"
30
  ldi r17, 0b00011111       ; zurück auf "7"
31
  cp r17, r21
32
  brne output
33
  reti
34
35
output:
36
  ldi r18, 1                ; Entprellhandler setzen
37
  out PORTB, r17            ; Ausgeben
38
  reti
39
 
40
main: 
41
  ldi r20, 1             ; eine  EINS
42
  ldi r19, 0                ; Timercounter
43
  ldi r18, 0                ; Entprellhandler
44
  ldi r17, 0b00011000      ; BCD-Basis-System
45
  ldi r16, 0b00000111      ; IO-Aufbau
46
  out DDRB, r16
47
  out PORTB, r17
48
  mov r21, r17
49
  ldi r16, (1<<CS02)       ; CS02 setzen: Teiler 256
50
  out TCCR0, r16
51
  ldi r16, (1<<TOIE0)      ; TOIE0: Interrupt bei Timer Overflow
52
  out TIMSK, r16
53
  sei
54
  rjmp loop
55
56
loop:
57
  rjmp loop           ; Warteschleife

lässt sich auch wunderbar überall simulieren, funktioniert also 
anscheinend einwandfrei. ^^
also danke noch mal.

eine frage stellt sich mir da aber natürlich trotzdem noch: ist das so 
vom stil her in ordnung? oder könnte/sollte/müsste man das evtl. noch 
ein wenig verändern, damit es irgendwo zuverlässiger läuft? (zeit ist 
bei dieser sache ja kein ding, beim tastendruck wird praktisch sofort 
umgesetzt, danach alle sekunde eines weitergeschaltet, also scheint mir 
soweit gut zu funktionieren!)

von Kluchscheißernder N. (kluchscheisser)


Lesenswert?

Sven J. schrieb:
> stimmt. das sei funktioniert einwandfrei!

Tipp: Bei www.atmel.com kann man das Datenblatt zum Controller 
(kostenfrei) herunterladen. Darin findet man neben der Liste der 
I/O-Register auch die Befehlsliste. Weiterführende Informationen zu den 
ASM-Befehlen findet man in der Online-Hilfe des AVR-Studios.

> (ich hatte das vorher schon
> mal probiert,

Das "Ausprobieren" der Instruktionen ist daher überflüssig.

> da hat es aber nicht so richtig funktioniert, wird wohl an
> den sprüngen gelegen haben!)

> ...

>
> eine frage stellt sich mir da aber natürlich trotzdem noch: ist das so
> vom stil her in ordnung? oder könnte/sollte/müsste man das evtl. noch
> ein wenig verändern, damit es irgendwo zuverlässiger läuft?

Ja, kann man.

Schau Dir mal die Bulletproof-Tastenentprellung von Peter Dannegger an. 
Sie entprellt (durch Vierfachabfrage im Timer-Interrupt) das Drücken und 
das Loslassen, selektiert die Flanke beim Drücken und ermöglicht auch 
eine sehr saubere Repeat-Funktionalität. Und da sie den Timer nicht 
stoppt oder anderswie manipuliert, kann der Timer nebenher auch noch für 
andere Dinge genutzt werden. Durch die Vierfachentprellung von Drücken 
und Loslassen können Störimpulse keinen versehentlichen Tastendruck 
auslösen, was derzeit in Deinem Programm der Fall ist.

Überdenke nochmal Deine Zählumfangsbegrenzung. Du sparst Dir die 
Abfragen, wenn Du den Zählerstand permanent mit 7 verANDest und die 
beiden starren Bits (interne Ziehwiderstände) dazu ORst.

> (zeit ist
> bei dieser sache ja kein ding, beim tastendruck wird praktisch sofort
> umgesetzt,

Und das ist das Problem, denn jeder Störimpuls ist in der Lage, einen 
Zählvorgang auszulösen. Deine "Entprellung" entprellt ja den ersten 
Tastendruck nicht, sondern wirkt nur als Repeat-Verzögerer.

> danach alle sekunde eines weitergeschaltet, also scheint mir
> soweit gut zu funktionieren!)

Der Schein trügt, im Simulator mag das funktionieren, in der Realität 
zählt es jeden Störimpuls.

Schau Dir das mal an:
Beitrag "Tasten entprellen - Bulletproof"
http://www.mikrocontroller.net/attachment/1925/Get8keyb.asm

MfG

von Kluchscheißernder N. (kluchscheisser)


Lesenswert?

Ich sehe gerade, das die Forensoftware den ersten Link verändert hat. 
Ich hatte den Link auf die Bulletproof-Entprellung eingefügt:
Beitrag "Tasten entprellen - Bulletproof"
und meine den ersten Beitrag des Threads.

MfG

von test (Gast)


Lesenswert?

Kluchscheißender Consulter schrieb:
> Tipp: Bei www.atmel.com kann man das Datenblatt zum Controller
> (kostenfrei) herunterladen. Darin findet man neben der Liste der
> I/O-Register auch die Befehlsliste. Weiterführende Informationen zu den
> ASM-Befehlen findet man in der Online-Hilfe des AVR-Studios.

Und wem das nicht reicht (oder wer kein AVR Studio, zwecks Hilfe nutzt), 
für den hat Atmel sogar noch was besseres:

http://atmel.com/dyn/resources/prod_documents/doc0856.pdf

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.