;**************************************** ; Ansteuerung einer Modelleisenbahn * ;**************************************** list p=16f84,r=dec include "p16f84.inc" __idlocs 1099h __config _XT_OSC & _WDT_OFF & _PWRTE_ON ;RAM-Adr D0 equ 16 ;"Digit", D0 = am weitesten rechts D1 equ D0+1 D2 equ D0+2 D3 equ D0+3 ;am weitesten links DTA equ D0+4 ;20,nichtinvertiert, Tasten-Status ta_change equ 21 ;Geänderte Tasten ta_press equ 22 ;Gerade gedrückte Tasten ta_timer equ 23 ;Zeit-Zähler für Autorepeat ;Für Division D_HI equ 24 D_LO equ D_HI+1 DIV equ D_HI+2 ;26 cl equ 27 ;Zähler für div16 u.v.a.m. str equ 28 ;Programmspeicher-Adressse des Strings ssave equ 30 ;STATUS-, W- Speicherzellen für INT wsave equ 31 anzeig equ 69 ;Anzeige-Zahl accel equ 70 acc_count equ 71 ;Geschwindigkeitsincrementteiler aktspd equ 72 ;Aktuelle Geschwindigkeit modus equ 73 ;Bit0,1: Modi, Bit6: Anzeige aktualisieren lassen (am Ende der ISR), Bit7: VZ speed equ 74 ;Betrag der Soll-Geschwindigkeit tmp equ 75 ;Temporäre Daten (Zähler) mwsave equ 76 ;W retten z1 equ 77 ;Für die Warteschleife z2 equ 78 z3 equ 79 ;Konstanten SD equ 7 ; {an LPT1 war's D6, Serielle Daten} SC equ 5 ; {an LPT1 war's D5, Serieller Takt} TA equ 6 ; {an LPT1 war's ACK, Tastenabfrage} A equ 2h ; Segmente A..H BE equ 8h CE equ 10h ;Buchstabe "C", C-Carry D equ 40h E equ 20h F equ 1h G equ 4h HE equ 80h ta1 equ 2 ;Von links oben nach rechts unten, Zeilen zuerst ta2 equ 1 ta3 equ 0 ta4 equ 3 ta5 equ 6 ta6 equ 7 ;****************************************************************** ;Initialisierung ORG 0000h goto main ORG 0004h INT: movwf wsave ;w und STATUS Reg. sichern movfw STATUS movwf ssave bcf STATUS,RP0 ;Bank 0 aktivieren btfss INTCON,2 ;Timer-INT goto no_timer bcf INTCON,2 ;Zähler-INT Flag löschen call check_key call periodic btfsc modus,6 call actualize bcf modus,6 ;jetzt ist es aktualisiert! no_timer: ; btfsc INTCON,0 ;INT PortB ; call int_TA movfw ssave ;w und Status wieder herstellen movwf STATUS swapf wsave,f ;von wsave nach w ohne STATUS verändern swapf wsave,w retfie ;****************************************************** ;Tables LEER: retlw 0; H: retlw 0; HA: retlw 0; HAL: retlw 0; HALL: retlw BE+CE+ E+F+G ;H ALLO: retlw A+BE+CE+ E+F+G ;A LLO: retlw D+E+F ;L LO: retlw D+E+F ;L O: retlw A+BE+CE+D+E+F ;O retlw 0; retlw 0; retlw 0; StOP: retlw A+ CE+D+ F+G ;S retlw D+E+F+G ;t retlw A+BE+CE+D+E+F ;O retlw A+BE+ E+F+G ;P PIEP: retlw A+BE+ E+F+G ;P retlw BE+CE ;I retlw A+ D+E+F+G ;E retlw A+BE+ E+F+G ;P number: addwf PCL,f ; pc+=w retlw A+BE+CE+D+E+F ; {0} retlw BE+CE ; {1} retlw A+BE+ D+E+ G ; {2} retlw A+BE+CE+D+ G ; {3} retlw BE+CE+ F+G ; {4} retlw A+ CE+D+ F+G ; {5} retlw A+ CE+D+E+F+G ; {6} retlw A+BE+CE ; {7} retlw A+BE+CE+D+E+F+G ; {8} retlw A+BE+CE+D+ F+G ; {9} ;***************** ;** UP Division ** ;***************** div16: ;FU: klassische vzl. Ganzzahldivision 16bit/8bit=8bit Rest 8bit ;PE: D_HI:D_LO=Dividend, DIV=Divisor (unverändert) ;PA: D_HI=Rest, D_LO=Ergebnis, bei Division durch Null D_LO=0FFh ;VR: D_LO, D_HI, cl, flags, w ;TA: 200..250 (inkl. return) movlw 8 movwf cl div16_loop: CLRC ;CY löschen rlf D_LO,f ;von rechts Null-Bit einschieben rlf D_HI,f ;Bit durchschieben SKPNC goto div16_overbit ;ein Bit wurde rausgeschoben! movfw DIV ;W mit Nenner laden subwf D_HI,w ;w=HI-DIV, CY=0 wenn HI zu klein SKPC goto div16_zerobit ;wenn HI zu klein war: Null-Bit div16_overbit: movfw DIV subwf D_HI,f ;HI-=DIV, Subtraktion wahr machen bsf D_LO,0 ;ein Bit setzen - das Ergebnis div16_zerobit: ;wird mitgeschoben! decfsz cl,f goto div16_loop ;und voilà: der Rest bleibt in HI einfach stehen! return ;*************************************************** ;** Zahlen-Ausgabe (8bit plus Vorzeichen) dezimal ** ;*************************************************** ;Die Codes gelangen in D3..D0, _ohne_ die Anzeige zu aktualisieren ;Führende Nullen werden unterdrückt, das Minus erscheint direkt vor der Zahl nmbout: ;PE: w (die auszugebende vzl. Zahl), VZ (Variable "modus" Bit 7) ;PA: D0..3 ;VR: w,FSR,D_LO,D_HI,DIV movwf D_LO movlw 10 ;16 für hex, digitweise inc funktioniert aber nicht movwf DIV movlw D0 movwf FSR digit_loop: clrf D_HI call div16 movfw D_HI call number ;Dezimalzahl sofort decodieren movwf INDF ;und in D3..0 abgelegt incf FSR,f movf D_LO,f skpz goto digit_loop btfss modus,7 goto clear_loop movlw G movwf INDF goto clear_loop_chk clear_loop: clrf INDF clear_loop_chk: incf FSR,f movfw FSR xorlw D3+1 skpz goto clear_loop return ;********************************************** strout: ;PE: W=Stringadresse ;VR: W, str(=W+4), cl movwf str ;str= ProgrammspeicherAdressse des Strings clrf cl bsf cl,2 ;also: Zähler:=4, ohne W einzusauen! movlw D3 movwf FSR ;ind? loop: movfw str call jmpw movwf INDF decf FSR,f ;nach rechts zu kleineren Digits incf str,f decfsz cl,f goto loop goto actualize jmpw: movwf PCL ;Trick17B! strout_wait: movwf mwsave call strout ; clrf z1 clrf z2 clrf z3 movlw 5 movwf z1 wait_loop: decfsz z3, 1 goto wait_loop decfsz z2, 1 goto wait_loop decfsz z1, 1 goto wait_loop movfw mwsave return ;************************************************************ actualize: ;PE: D0..DT=Bit-Daten ;VR: Zero, - (die Daten werden einmal komplett herumgeschoben) comf DTA,f ;DTA invertieren movlw 41 ;8 Bits pro Byte und 4 Bytes, +1 um Ausgangszustand in reg. movwf cl bcf STATUS,C ;zuallererst eine Null einschieben act_loop: rlf DTA,f rlf D3,f bcf PORTB,SC ;und Takt auf LOW rlf D2,f rlf D1,f rlf D0,f ;und was hier herauspurzelt, das brauchen wir bsf PORTB,SD ;Datenpin auf High voreinstellen (wegen Invertierung) SKPNC bcf PORTB,SD ;Bei Eins-Bit Datenleitung auf LOW decfsz cl,f goto act_1 nopi: nop comf DTA,f ;urspr. wert return act_1: bsf PORTB,SC ;nun Takt auf HIGH goto act_loop ;*********************************************************** ;Routinen ;Auslesen des Tasten-Schieberegisters und Speichern in DTA und ta_change ; ;PE: DTA (Vorheriger Tasten-Status), PORTB-TA (Tasteneingang) ;PA: DTA (aktueller Tasten-Status), ta_change (Status-Veränderung) ;VR: DTA, ta_change, ta_press (temporär) (w,STATUS) fnd_TA: bcf STATUS,Z btfsc PORTB,TA ;Abfrage: Neue Taste gedrückt? movf DTA,f ;Wenn nein, DTA gleich 00, Ausgangszust. ? skpnz return bsf modus,6 ;Anzeige aktualisieren lassen clrf ta_press ;Hier: Schleifenzähler bsf ta_press,3 ;mit 8 laden bsf PORTB,SD oneshift: bsf PORTB,SC ;Tasten-SR mit Einsen laden bcf PORTB,SC decfsz ta_press,f goto oneshift bcf STATUS,C bcf PORTB,SD ;DATA auf Null bsf PORTB,SC ;eine Null einschieben bsf PORTB,SD clrf ta_press ;Hier: Maske für Tastenstatus-Abfrage bsf ta_press,0 ;=1 clrw bcf STATUS,C zeroshift: bcf PORTB,SC btfss PORTB,TA iorwf ta_press,w ;Ergebnis in w ablegen rlf ta_press,f ;Maske nach links schieben bsf PORTB,SC skpc ;wenn aus maske 1 rausgeschoben goto zeroshift bcf PORTB,SC ;Nun befindet sich in w das neue DTA movwf ta_press ;abgelegen (temporär) xorwf DTA,w ;Änderungen ermitteln movwf ta_change ;geänderte Bits in ta_change speichern movfw ta_press movwf DTA return ;*********************************************** ;ISR check_key: call fnd_TA ;liefert neues DTA und ta_change movf ta_change,f ;Änderungen? skpz goto ta_changed ;ja! decfsz ta_timer,f ;Wenn nein, Autorepeat-Zähler verringern, --timer_cl==0? return ;Wenn Autorepeat-Zähler noch nicht Null, nichts tun movfw DTA ;Alle Tasten "automatisch" noch einmal drücken lassen (ta_change=DTA) movwf ta_change movlw 4 ;Timer mit 4 laden (Wiederhol-Frequenz) goto ta_0 ta_changed: movlw 16 ;Timer mit größerem Wert laden (Wiederhol-Verzögerung) ta_0: movwf ta_timer movfw ta_change andwf DTA,w ;w:=DTA and ta_change, eff.Änd. bei mehrfachtastendruck movwf ta_press goto ta_behand set_anzeig: ;Anzeige setzen, je nach Modus, Vorzeichen und "w" ;PE: modus: Modus-Buchstabe und Vorzeichen ; w: anzuzeigende Zahl movwf anzeig call nmbout btfss modus,7 goto make_p movlw 100 subwf anzeig,w skpnc return make_p: movlw A+BE+ E+F+G+HE ;P. btfsc modus,0 movlw A+ +D+E+F+G+HE ;E. btfsc modus,1 movlw A+ +CE+D+ F+G+HE ;S. movwf D3 return ;*********************** ;** Tasten-Behandlung ** ;*********************** ;Wird bei jedem Tastatur-Ereignis (KeyPress, KeyRepeat und KeyRelease) aufgerufen ;Die Anzeige wird anschließend vom Aufrufer aktualisiert (je nach D3..D0) ta_behand: btfsc ta_press,ta1 goto mta1 ;Modus btfsc ta_press,ta4 goto mta4 ;Null und Nothalt btfsc modus,0 goto ta_behand_e btfsc modus,1 goto ta_behand_s ;hier Tastaturbehandlung für Modus "P" clrw btfsc ta_press,ta2 movlw 120 btfsc ta_press,ta3 movlw 240 btfsc ta_press,ta5 movlw 60 btfsc ta_press,ta6 movlw 180 iorlw 0 skpnz return ;für KeyRelease sowie die beiden nicht bestückten Tasten setspd: movwf speed ;Für Hauptprogramm übernehmen call set_anzeig clrf acc_count bsf acc_count,0 return mta1: incf modus,f ;P->E->S->? movfw modus xorlw 3 andlw 3 movlw 0FCh skpnz andwf modus,f ;Bits löschen, zurück zu P movfw speed btfsc modus,1 movfw accel call set_anzeig return mta4: movf aktspd,1 ;if (!aktspd) goto lok_steht skpnz goto lok_steht movf speed,1 ;if (speed) goto lok_bremst skpz goto lok_bremst clrf aktspd ;else Not_aus movlw StOP goto strout lok_steht: movlw 80h ;Vorzeichen-Bit (Bit7) gesetzt (Vorbereitung für XOR) xorwf modus,1 ;Nur bei Stillstand Vorzeichen wechseln lok_bremst: movlw 0 goto setspd ta_behand_e: ;Modus "E" clrw btfsc ta_press,ta2 movlw 10 btfsc ta_press,ta3 movlw 1 btfsc ta_press,ta5 movlw -10 btfsc ta_press,ta6 movlw -1 iorlw 0 skpnz return ;für KeyRelease sowie die beiden nicht bestückten Tasten addwf speed,f andlw 80h ;War W negativ? skpz goto underflowtest ;Test auf max. 240 (= F0h) movfw speed addlw -240 movfw speed skpnc movlw 240 goto setspd underflowtest: movfw speed skpc clrw goto setspd ta_behand_s: ;Modus "S" clrw btfsc ta_press,ta2 goto piep1 btfsc ta_press,ta3 movlw 1 btfsc ta_press,ta5 goto piep2 btfsc ta_press,ta6 movlw -1 iorlw 0 skpnz return ;für KeyRelease sowie die beiden nicht bestückten Tasten addwf accel,f movlw 7 decf accel,f andwf accel,f incf accel,f movfw accel goto set_anzeig piep1: movlw 0 goto piep piep2: movlw 190 piep: movwf z3 movlw PIEP call strout movfw z3 clrf z1 ;256 Schwingungen piepl: bsf PORTB,4 ;Buzzer HIGH movwf z2 piepf1: decfsz z2,f goto piepf1 bcf PORTB,4 ;Buzzer LOW movwf z2 piepf2: decfsz z2,f goto piepf2 decfsz z1,f goto piepl return ;******************** ;** Timer-Prozedur ** ;******************** ;Wird periodisch mit ca. 78Hz aufgerufen ;Die Anzeige wird _nicht_ anschließend vom Aufrufer aktualisiert - das muss man ggf. selber machen periodic: ;78 Hz decfsz acc_count,f return movfw accel movwf acc_count movfw aktspd subwf speed,0 skpnz return ;Mach nix, wenn Geschw. erreicht movlw 2 skpc ;c gesetzt --> aktspd < speed --> aktspd++ movlw -2 addwf aktspd,f movf DTA,f ;Nichts Ausgeben, wenn Taste noch gedrückt skpz return movfw aktspd call set_anzeig bsf modus,6 ;aktualisieren lassen return ;********************************************** Hauptprogramm main: bsf INTCON,5 ;Bit5=INT durch Zähler clrf PORTA ;Ausgänge 0 Setzen bcf PORTB,4 ;Buzzer-Ausgang auf LOW bsf STATUS,RP0 ;Bank 1 aktivieren bcf TRISB,SC ;Tristate ausschalten --> Ausgang, nach Reset --> 0 bcf TRISB,SD ;dito bcf TRISB,4 ;Buzzer bcf TRISA,2 ;Ausgänge anschalten bcf TRISA,3 movlw 01000110b ;Bit7=0: Pullups EIN, Bit2..0: Vorteiler auf 128:1, --> intern ca. 20 kHz Takt movwf OPTION_REG ;Bit3=0: am Zähler, Bit5: 1->Zähler, 0->Zeitgeber, Bit4&6: unwichtig bcf STATUS,RP0 ;Bank0 clrf DTA clrf modus ;Modus: P, VZ: 0 clrf speed clrf aktspd movlw 3 movwf accel ;Hallo-Begrüßung (Marquée) movlw 9 movwf tmp movlw LEER movwf mwsave mwloop: call strout_wait incf mwsave,f movfw mwsave decfsz tmp,f goto mwloop movlw 0 call setspd call actualize bsf INTCON,7 ;Bit7=Globale INT-aktiv ;Hauptschleife macht nur Pulsweitenmodulation (=PWM) pwm_loop: movfw aktspd ;Geschwindigkeitsabhängige Pulsweitenmodulation subwf TMR0,w skpnc ;c gesetzt --> TMR0 < Speed --> Ausgeben! goto set0 btfsc modus,7 ;Vorzeichen gelöscht? bsf PORTA,2 btfss modus,7 ;Vorzeichen gesetzt? bsf PORTA,3 goto pwm_loop set0: bcf PORTA,2 ;Ausschalten bcf PORTA,3 goto pwm_loop end