PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Tiny11 - Tastenabfrage



gulliver
22.05.2007, 13:29
Hallo Jungs
Irgendwie bin ich jetzt ziemlich ratlos und weiß nicht mehr weiter.
Habe mir ein Netzteil für meine Kameras gebaut, wo ich die unterschiedlichen Spannungen mittels Tiny11 wähle.
Normalerweise würde eine simple Widerstandsumschaltung mit Drehschalter o.ä. völlig ausreichen, aber mein Spieltrieb hat mir diesen Floh ins Ohr gesetzt. Hintergrund ist auch, daß ich nicht aus Versehen die Spannung umschalte, während eine Kamera dran hängt. Deswegen habe ich auch den Tasteninterrupt nach Ablauf der Zeit oder nach erkanntem Tastendruck abgeschaltet, so daß Fehlbedienungen ausgeschlossen sind. Eine andere Spannung kann erst nach Neueinschalten gewählt werden.

Das Programm dürfte wegen der üppigen Kommentare gut durchschaubar sein. Die Belegung der Pins sieht man im Kopf.

Nun zu meinem Problem:
Im Simulator macht es, was es soll, aber in der Schaltung schaltet die 3V-LED, die ich als Default-Spannung gewählt habe, nur einmal ein und dann wird gleich auf die 4,3V (PB2) umgeschaltet, ohne einen Taster gedrückt zu haben.

Zuerst dachte ich an einen Schaltungsfehler. Darum habe ich den Tiny mit Tastern und LED's auf ein Steckbrett gesetzt und da passiert genau dasselbe. Jetzt bin ich so ziemlich ausgelaugt und habe keine Ideen mehr und hoffe, daß jemand einen Tip für mich hat.

Danke schon mal im Voraus

Roger



;************************************************* *******************
; Netzteil für Kameras, 3V, 4,3V und 6V
;
; MCU: ATtiny11; interner Takt=1MHz
; Version: 1.2
; Entprellroutine im Timer-Interrupt
;
; - Taster-4,3V an PB2 (Pin7)
; - Taster-6V an PB1 (Pin6)
; - Schaltausgang 3V über PB0 (Pin5)
; - Schaltausgang 4,3V über PB3 (Pin2)
; - Schaltausgang 6V über PB4 (Pin3)
;
; - T-Bit fungiert als Merker für Taste entprellt und Zeitablauf
;************************************************* *******************
;
.nolist
.include "C:\Programme\AVR Tools\AvrAssembler2\Appnotes\tn11def.inc"
.list
;
; Konstanten
.equ key_port = PINB ; Eingabe
.equ t_43v = PB2 ; Taster-4,3V
.equ t_6v = PB1 ; Taster-6V
.equ t_maske = 0b00000110 ; für Tastenmaskierung
.equ schalt_port = PORTB ; Ausgabe
.equ a_3v = PB0 ; Schaltausgang für 3V
.equ LED_default = PB0 ; zugehörige LED
.equ a_43v = PB3 ; Schaltausgang für 4,3V
.equ a_6v = PB4 ; Schaltausgang für 6V
.equ ext_int = 0b00100000 ; Maskierung für PinChange Interrupt
.equ schlafen = 0b00100000 ; Sleep-Modus erlauben
.equ timerwert = 255-15 ; etwa 15ms
.equ zeitlimit = 3 ; 255 x15ms x3 ca.11sek, Zeit für Spannungswahl
.equ zeit_toggle = 3 ; 3x 15ms=45ms, Blinkintervall der 3V-LED
; leuchtet dauernd, wenn Zeit abgelaufen ist
; Register
.def Save = R2 ; wegen fehlendem Stack
.def key_ct0 = R3
.def key_ct1 = R4
.def key_state = R5
.def key_press = R6

.def mp = R16 ; Arbeitsregister
.def merker_t = R17 ; merken,ob Taste gedrückt wurde
.def zeit_L = R18 ; Gesamtzeit
.def zeit_H = R19
.def Blinker = R20 ; Zähler für Blinkintervall
;
; Interruptvektoren
.cseg
rjmp main ; Programmstart
reti ; externer Interrupt für Flankensteuerung
rjmp taste ; Pin change handler
rjmp timer ; Timer0 overflow handler
reti ; Analog Comparator handler
;
;************ Interrupt-Routinen *********************************
taste:
in Save,SREG ; Stack sichern

in merker_t,key_port ; Taste sichern, falls eine gedrückt wurde ;R1
andi merker_t, t_maske ; nur Tasten maskieren
ldi mp,0
out GIMSK,mp ; externe Interrupts abschalten; Neuwahl einer
; Spannung ist nur nach Neueinschalten möglich
out SREG,Save ; Stack wieder zurückschreiben
reti
;
timer:
in Save,SREG ; Stack sichern

tst merker_t ; wurde eine Taste gedrückt?
breq weiter ; nein, Entprellung überspringen
;
in mp, key_port ; Taste sichern, falls eine gedrückt wurde ;R1
andi mp, t_maske ; nur die Tasten maskieren
com mp ; low active
eor mp, key_state
and key_ct0, mp ; reset counter
and key_ct1, mp
com key_ct0 ; count
eor key_ct1, key_ct0
and mp, key_ct0 ; input AND last counterstate
and mp, key_ct1
eor key_state, mp ; toggle state
and mp, key_state ; 0-1-transition
or key_press, mp ; store key press detect
tst key_press ; Taste schon festgemacht?
breq weiter ; nein, noch eine Runde
com key_press
zeitende:
out SREG,Save
set ; T-Flag setzen und tschüß
rjmp ende
;
weiter:
dec Blinker ; Blinkzeit runterzählen
brne weiter_ ; wenn runtergezählt ist,
ldi Blinker,zeit_toggle ; dann neuladen und LED umschalten

sbis schalt_port,LED_default ; überspringe,wenn LED aus ist
rjmp led_aus
;
cbi schalt_port,LED_default ; LED einschalten
rjmp weiter_
;
led_aus:
sbi schalt_port,LED_default ; LED ausschalten
;
weiter_:
dec zeit_L ; Zeit runterzählen
brne raus ; wenn Nulldurchlauf,
dec zeit_H ; H-Teil runterzählen
breq zeitende ; Zeit abgelaufen
raus:
out SREG,Save ; Stack wieder zurückschreiben
ende:
ldi mp,1<<PCIF ; Flag für PinChange-Interrupt löschen,
out GIFR,mp ; da es auch auf die Schaltausgänge reagiert
reti
;
;************** Unterprogramme **************************************
;hier wird die gedrückte Taste ermittelt und die entsprechende Spannung
;eingeschaltet. Interrupts werden abgeschaltet

auswerten:
cli ; Interrupt löschen
ldi mp,0 ; Timer abschalten, wird nicht mehr benötigt
out TIMSK,mp

clt ; T-Flag löschen
clc ; zuerst C-Flag löschen
ror key_press ; Merker zwecks Prüfung 1.Taste 2x rollen
ror key_press
brcc u6v ; Bit1 nicht gesetzt? dann auf 6V testen
cbi schalt_port,a_43v ; 4,3V einschalten; L, da PNP-Transistor
sbi schalt_port,a_3v ; 3V-LED abschalten
rjmp ok ; ... und raus
;
u6v:
ror key_press ; Merker zwecks Prüfung 2.Taste eins weiter
brcc default ; Bit2 nicht gesetzt? dann 3V einschalten
cbi schalt_port,a_6v ; 6V einschalten; L, da PNP-Transistor
sbi schalt_port,a_3v ; 3V-LED abschalten
rjmp ok
default:
cbi schalt_port,a_3v ; 3V-LED einschalten; sonst nichts
ok:
rjmp loop ; ... und fertig
;
;************** Hauptprogramm ************************************
main:
; es ist kein Softwarestack vorhanden, daher keine Call-Befehle!!!
; Hardware initialisieren
sbi schalt_port,a_3v ; alle Ausgänge ausschalten
sbi schalt_port,a_43v ; H, da alle Transistoren PNP sind!!!
sbi schalt_port,a_6v
sbi DDRB,a_3v ; Pin2,3 und 5 als Ausgang
sbi DDRB,a_43v
sbi DDRB,a_6v

cbi DDRB,t_43v ; PB1 und PB2 als Tastereingänge setzen
cbi DDRB,t_6v

ldi mp,(1<<CS00)|(1<<CS02) ;Teiler=1024 (entspr.etwa 1kHz=1ms)
out TCCR0,mp

ldi mp,timerwert ;Timer vorladen
out TCNT0,mp

ldi mp,1<<TOIE0 ;Timerinterrupt einschalten
out TIMSK,mp

ldi mp,ext_int
out GIMSK,mp ; externe Interrupts ermöglichen

ldi mp,schlafen ; Schlafmodus und Interrupt
out MCUCR,mp ; und ins Controlregister schreiben

clr key_state ;sämtliche Merker der
clr key_press ;Entprellung löschen
clr key_ct0
dec key_ct0
mov key_ct1, key_ct0

ldi zeit_H,zeitlimit ; Gesamtzeit bis Aktivierung festlegen
cbi schalt_port,0 ; Blink-LED einschalten
ldi Blinker,zeit_toggle ; Togglezeit für LED-Blinken
ldi mp,1<<PCIF ; Flag für PinChange-Interrupt löschen,
out GIFR,mp ; da es auch auf die Schaltausgänge reagiert
sei ; Interrupt global freigeben
;
loop:
sleep ; CPU schlafen legen
brts auswerten ; eine Taste gedrückt; wenn ja,auswerten
rjmp loop ; ... und wieder schlafen ...
;
.exit ; Programmende


edit:
Habe noch den Schaltplan des Netzteils beigepackt.

wkrug
22.05.2007, 21:46
Du benutzt die gleichen Register im Interrupt als auch im Main Programm.

Ich würde für den Interrupt exklusiv eigene Register verwenden, die im Hauptprogramm dann überhaupt nicht genutzt werden.

Es könnte ja bei Deiner Programmierung passieren, das Du in das Register mp (r16) eine Wert legst.
Nun tritt ein Interrupt auf, der Wert von mp wird verändert und das Hauptprogramm läuft dann mit dem geänderten Wert von mp weiter!

Ich denke auch das das genau dein Problem ist.

gulliver
23.05.2007, 08:12
Im Hauptprogramm passiert aber nix, so daß es keine Registerkonflikte geben kann. Das UP wird aber nur einmal aufgerufen, einmal, wenn die Zeit abgelaufen ist oder wenn ein Tastendruck erkannt und die Taste entprellt wurde. Dann wird das entsprechende Relais und damit auch die parallele LED eingeschaltet und das war's.

Sämtliche Interrupts werden abgeschaltet und der MC ist für die Außenwelt taub.

Ich habe keine Erklärung für das Umschalten. Die Flagge lösche ich noch im Timerinterrupt, da sie ja auch beim Umschalten eines Ausganges gesetzt wird.

mfg Roger

wkrug
23.05.2007, 19:49
Ich kenn jetzt natürlich deine Tastenansteuerung nicht, könnte es sein, das Dir da ein Lapsus mit den Pull Up Widerständen unterlaufen ist ?
Wenn die drin sind ist der Port auf high bis ein Taste gedrückt wird.

Wenn ich dein Programm aber überschaue ertwartet es High impulse um umzuschalten.

Wenns im Simulator läuft fällt mir auch nichts gescheites mehr ein.

gulliver
24.05.2007, 08:26
Die Tastensteuerung ist in der Schaltung zu sehen. Es ist eine stinknormale Tastenschaltung mit externen Pull Up's gegen +. Die Taster ziehen nach Masse.

Als ich das letzte Mal auf dem Steckbrett probiert habe, hatte ich die +5V aus Versehen nicht am Chip, sondern außerhalb genau daneben gesteckt, also der Chip nur an Masse und der Rest über die Widerstände an +. Da verhielt er sich genauso, wie in der fertigen Schaltung. Ich gehe mal davon aus, daß er eine Macke hat, also über den Jordan mit ihm.

Nun werde ich einen anderen besser ausgestatteten MC nehmen, vor allem mit Stack. Diese Rumjumperei ist doch Murks. Hatte den Tiny11 noch rumliegen und dachte, für diesen Zweck reicht er. Solche mickrigen Teile werde ich mir nie wieder aufhalsen.

Lange Rede, kurzer Sinn, ich mache diesen Thread zu.

mfg Roger

wkrug
24.05.2007, 21:44
Dann nimm aber gleich einen der 2 richtige Interrups hat und nicht dieses doofe Pin Change Zeug.
Dann kannst Du Dir auch gleich die Tastenentprellung sparen, wenn du die Interrupts während des Interruptaufrufes sperrst.

gulliver
03.06.2007, 10:10
Gut, daß ich reingeschaut habe.

Ich bin doch ein selten blödes Rindvieh ](*,). Beim Testen im Debugger habe ich die Taster immer gesetzt,d.h., auf H-Pegel, aber die schalten nach Masse. Weil ich die Eingabe vor der Entprellung negiert hatte, konnte nur Mist rauskommen. Es war ein reiner Denkfehler. Wenn man die Tastenbits noch vor der Interruptfreigabe setzt und danach das Flag wieder zurücksetzt, kommt man schon ein Stück weiter. Wenn man die internen Pullup's nimmt, hat man damit keine Probleme. Da ich die Hardware schon mit externen Pullup's gebaut hatte und nur noch der Prozessor zu programmieren war, mußte ich mich beim Proggen auch darauf einstellen.

Weiterhin hatte ich auch noch ein Timerproblem mit dem Vorteiler. Bisher hatte ich das Vorteilerbit und Teilerwert immer in einem Stück ins CLKPR-Register geschrieben, etwa so:
ldi mp, 0b10001000
out CLKPR,mp
es muß aber nacheinander geschrieben werden. So habe ich auch das Datenblatt gedeutet. Irritiert wurde ich durch den Debugger, weil er das Vorteilerbit beim Einschreiben des Wertes wieder gelöscht hatte. Es ist mir erst beim Überprüfen mit dem Oszi aufgefallen, wo ich mir über den Takt gewundert habe, der überhaupt nicht mit meinen Berechnungen übereinstimmte. Beim Rückrechnen bin ich auf den Ursprungstakt von 4.8MHz gekommen, es wurde also nicht's geteilt.

Na ja, was soll's, das Problem ist gelöst und wieder etwas dazugelernt. Falls es jemandem interessiert, das Programm habe ich beigelegt damit auch andere Anfänger mal reinschnuppern und vielleicht noch was dazulernen können. Reichlich kommentiert ist es auch und noch dazu in deutsch ;-).

Übrigens, was hast du gegen den PinChange Interrupt? Ich benutze ihn ganz gerne, aber es sollte nicht so ein abgespeckter Murks sein, wie beim Tiny11. Da muß man ja das Flag jedesmal löschen, wenn man ein Portpin geändert hat.
So wie es beim tiny13, bei anderen bestimmt auch, mit der Maskierung gelöst ist, finde ich es gut. Da beeinflussen die nichtmaskierten Portpins nicht's mehr.

Was aber nicht geklappt hat, ist die Entprellung im PinChange Interrupt selbst. Da mußte ich dann umstricken. Hier noch der Code, falls es jemandem interessiert:

;************************************************* *****************************
; Netzteil für Kameras mit Entprellroutine von P.Dannegger
; MCU: ATtiny13
; Takt: 4.8 MHz
; Version: 1.0
;
; Default-LED (3V): PB0
; Taster-4,3V: PB2 Schaltausgang: PB3
; Taster-6V: PB1 Schaltausgang: PB4
;************************************************* *****************************
.nolist
.include "tn13def.inc"
.list
;
;***** Konstanten *****
.equ vorteiler = 0b10001000 ;Wert für Systemtakt 256 (MCU-Takt=37kHz)

.equ keyport = PINB ;Eingänge
.equ t_43v = 2 ;Bitpositionen für Tasten 4,3V
.equ t_6v = 1 ;und 6V

.equ schaltport = PORTB ;Ausgänge
.equ a_3v = 0 ;Bitpositionen für Schalten 3V
.equ a_43v = 3 ;4,3V
.equ a_6v = 4 ;und 6V

.equ t_maske = 0b00000110 ;Maske für Tasten
.equ portmaske = 0b00011001 ;Maske für Ein- und Ausgänge

.equ startwert = 256-6 ;Startwert für Zeit, etwa 300ms
.equ count = 30 ;10 Sekunden bis Zeitablauf
;
;***** Register *****
.def key_ct0 = R2
.def key_ct1 = R3
.def key_state = R4
.def key_press = R5
.def Counter = R6 ;Zeitzähler

.def mp = R16
.def leds = R17
;
;******** Interruptvektoren **********
.org 0
rjmp init ;Sprung zum Hauptprogramm
reti ;externer Interrupt für Flankensteuerung
rjmp tasten ;Interrupt für Taster
rjmp zeit ;TIM0_OVF, Timer/Counter0 Overflow
reti ;EE_RDY, EEProm ready
reti ;ANA_COMP, Analog Comparator
reti ;TIM0_COMPA, Timer/Counter0 Compare Match A
reti ;TIM0_COMPB, Timer/Counter0 Compare Match B
reti ;WDT Time-out
reti ;ADC Konvertierung komplett
;
;******** Interrupt-Routinen **********
;*** Tastenerkennung ***
tasten:
in mp,SREG
push mp

ldi mp,0 ;Timer-Interrupt abschalten
out TIMSK0,mp

pop mp
out SREG, mp
set ;setze T-Flag
reti
;
;*** Zeit für Spannungswahl ***
zeit:
in mp,SREG
push mp

dec Counter ;Zeit runterzählen
brne nochmal ;wenn runtergezählt...,

pop mp
out SREG, mp
set ;...dann T-Flag setzen
rjmp timer_finit ;und fertig

nochmal:
ldi mp,startwert ;Timer mit Startwert laden
out TCNT0,mp

sbic schaltport,a_3v ;LED blinken lassen
rjmp aus

sbi schaltport,a_3v
rjmp timer_raus
aus:
cbi schaltport,a_3v
timer_raus:
pop mp
out SREG, mp
timer_finit:
reti
;
;******** Unterprogramme **********
auswerten:
cli ;alle Interrupts
clt ;und T-Flag löschen

ldi mp,0 ;PinChange-Interrupt abschalten
out GIMSK,mp

ldi mp,0 ;Timer-Interrupt abschalten
out TIMSK0,mp

entprelle:
rcall entprellen ;Taste entprellen
rcall pause ;kurze Pause

tst key_press ;Taste schon entprellt?
breq entprelle ;nein,nochmal

mov mp,key_press ;wegen dummen Register
andi mp,t_maske ;nur Tasten maskieren

clc ;C-Flag aus
ror mp ;für erste Taste 2x rotieren
ror mp
brcc taste2 ;C gesetzt? nein,es war die andere Taste
cbi schaltport,a_6v ;6V einschalten
sbi schaltport,a_3v ;blinkende LED abschalten
rjmp ende
ret

taste2:
ror mp ;noch einmal rotieren
brcc weiter ;C gesetzt? wenn nein,Zeitablauf
cbi schaltport,a_43v ;4,3V einschalten
sbi schaltport,a_3v ;blinkende LED abschalten
rjmp ende

weiter: ;Zeit war abgelaufen, es bleibt bei 3V
cbi schaltport,a_3v ;blinkende LED ständig einschalten

ende:
ret
;
entprellen: ;Entprellroutine
in mp, keyport
com mp ;low active
eor mp, key_state
and key_ct0, mp ;reset counter
and key_ct1, mp
com key_ct0 ;count
eor key_ct1, key_ct0
and mp, key_ct0 ;input AND last counterstate
and mp, key_ct1
eor key_state, mp ;toggle state
and mp, key_state ;0-1-transition
or key_press, mp ;store key press detect
ret
;
pause: ;kurze Pause von 10ms
ldi mp, 188
loop:
dec mp
brne loop
ret
;
;******** Hauptprogramm **********
init:
ldi mp,low(RAMEND) ;der übliche Stack
out SPL,mp

ldi mp,(1<<clkpce)
out CLKPR,mp ;Vorteilerbit setzen

ldi mp,(1<<clkps3) ;Vorteiler auf 256 (Takt durch 256)
out CLKPR,mp

ldi mp,(1<<CS02)|!(1<<CS01)|(1<<CS00)
out TCCR0B,mp ;Teiler 1024 (Timertakt=36Hz=27ms)

ldi mp,startwert ;Timer mit Startwert laden
out TCNT0,mp

ldi mp,1<<TOIE0 ;Timer-Interrupt einschalten
out TIMSK0,mp

ldi mp,1<<PCIE ;PinChange-Interrupt einschalten
out GIMSK,mp

ldi mp,t_maske ;Tasten in PinChange-Maske eintragen
out PCMSK,mp

ldi mp,1<<SE ;Schlafmodus einschalten
out MCUCR,mp

ldi mp,portmaske ;Tastereingänge und Schaltausgänge
out DDRB,mp

sbi schaltport,a_3v ;Ausgänge ausschalten (PNP-Transistoren)
sbi schaltport,a_43v
sbi schaltport,a_6v

clr key_state ;Merker der Entprellroutine löschen
clr key_press
clr key_ct0
dec key_ct0
mov key_ct1, key_ct0
clt ;T-Flag löschen

ldi mp,count ;Zeitzähler laden
mov Counter,mp

sei ;Interrups freigeben
;
main:
brtc nix ;T-Flag gesetzt?
rcall auswerten ;wenn ja, auswerten
nix:
sleep ;abnicken
rjmp main
.exit ;Programmende
;


Nun mache ich die Luke endgültig zu, schaue aber gelegentlich mal rein, falls jemand was zu mäkeln hat;-) oder auch einen Tip. Ich stehe ja noch ziemlich weit unten auf der Erfahrungstreppe und für jeden Tip offen.

mfg Roger