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?
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.
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.
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
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. :-)
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
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.
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?
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
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
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
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!)
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
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
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