;;======================================================================;;
;; SimpleMaus ;;
;;======================================================================;;
;; ;;
;; Program: SimpleMaus -- Lokmaus throttle ;;
;; Code: Paco Cañada ( http://www.fut.es/~fmco ) ;;
;; Platform: Microchip PIC16F628, 4 Mhz ;;
;; Date: 01.02.2006 ;;
;; First release: 16.02.2006 ;;
;; LastDate: 16.08.2018 ;;
;; ;;
;;======================================================================;;
;Tab width = 8, EOL = LF, Charset = ANSI (required to be binary compatible to ñ character in EEPROM space ... brr)
; This program is for a simple LokMaus throttle (Lenz XBus).
; It uses two LED display with only a common resistor (only one segment light at the same time)
; Uses PWM output (19200Hz) for analog voltage output that is compared with potentiometer voltage
; in 32 steps
; Processor PIC 16F628 running at 4MHz internal oscillator.
;
; Revisions:
;
; 01/02/2006 Start of writing code.
; 07/02/2006 First prototype. RA4 need external pull-up. Communication works
; 10/02/2006 Read pot. routines, added setup pot. and display
; 12/02/2006 Added display flashing
; 13/02/2006 Added save current loco
; 14/02/2006 Added stabilization time on reconection
; 16/02/2006 Changed direction / steps menu and clear keys status on start-up
; 31/10/2006 Change direction directly on pushbutton. F1 and F2 supported.
; Hardware: Four keys, one potentiometer,
; two 7-segment displays (dp not used), one RS485 transceiver
;RA0 17 Key "Sel", Segment "a"
;RA1 18 AN1 lowpass filtered PWM for analog comparator
;RA2 1 AN2 potentiometer to analog comparator
;RA3 2 First digit, common cathode
;RA4 3 Second digit, common cathode
;RA5 4 (!MCLR) Keypress detect, H-active (This pin is input-only)
;RA6 15 Key "F1/-", Segment "g"
;RA7 16 Key "F0/+", Segment "f"
;RB0 6 MAX485 driver signal, H=send, L=receive
;RB1 7 RX MAX485 Rexeive
;RB2 8 TX MAX485 Send
;RB3 9 CCP1 PWM output for A/D conversion
;RB4 10 Key "STOP", Segment "b"
;RB5 11 Segment "c"
;RB6 12 Segment "d"
;RB7 13 Segment "e"
; ----- Definitions
#define __VERNUM 0x02
#define __VERREV " "
#define __VERDAY 0x30
#define __VERMONTH 0x10
#define __VERYEAR 0x06
list p=16F628,r=DEC
errorlevel -302
errorlevel -306
INCLUDE "P16F628.INC"
__FUSES _BODEN_ON & _CP_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLRE_OFF & _INTRC_OSC_NOCLKOUT
; ----- Macros
#define DNOP goto $+1
; ----- Constant values
FXTAL equ 4000000 ; Internal oscillator frequency
BaudRate equ 62500 ; XBus serial speed
; --- Xbus constants
BROADCAST equ 0x60 ; broadcast call byte
; --- Display constants
MENU_RUN equ 0x00
MENU_LOK equ 0x04
MENU_DIR equ 0x08
MENU_SETUP equ 0x0C
MENU_XBUS equ 0x10
MENU_POT equ 0x14
MENU_FNC equ 0x18
CHR_P equ 0x0D
CHR_E equ 0x0E
CHR_SPC equ 0x0F
CHR_L equ 0x10
CHR_D equ 0x11
CHR_A equ 0x12
; --- EEPROM Section
ADR_MEM equ 0x00 ; Xbus address EEPROM location
LOK_MEM equ 0x01 ; Last loco used
POT_MEM equ 0x02 ; Potentiometer offset
; ----- Variables
; --- Internal RAM Section
; --- Top on all banks
INT_W equ 0x70 ; Interrupt variables.
INT_STAT equ 0x71
INT_PCLATH equ 0x72
INT_FSR equ 0x73
EEDATA0 equ 0x7B ; EEPROM shadow variables
EEADR0 equ 0x7C
; --- Bank 0
BUFRXINI equ 0x20 ; first input buffer location
HEADER equ 0x20 ; Xbus packet
DATA1 equ 0x21
DATA2 equ 0x22
DATA3 equ 0x23
DATA4 equ 0x24
DATA5 equ 0x25
;
BUFRXEND equ 0x31 ; last reception buffer location
KEYFLAG equ 0x35 ; Keyboard flags
R0 equ 0x38 ; General & Bin to BCD registers
R1 equ 0x39
LSB equ 0x3A
MSB equ 0x3B
DUMMY equ 0x3C ; Temporary values
TEMP equ 0x3D
TEMP2 equ 0x3E
COUNT equ 0x3F
CALLBYTE equ 0x40 ; call byte byte from command station
MY_ADR equ 0x41 ; XpressNet Address for normal inquiry messages
TXPOS equ 0x42 ; Serial Buffer output pointers
TXCNT equ 0x43
RX_XOR equ 0x44 ; Serial incoming checksum
RX_LEN equ 0x45 ; Incomming message length
XFLAGS equ 0x46 ; XpressNet flags
XBUS_VER equ 0x47 ; XpressNet version
XBUS_STA equ 0x48 ; Command Station type
FLAGS equ 0x49 ; Flags
DCC_STATUS equ 0x4F ; Command Station Status
CURRMTR equ 0x52 ; Current Loco Multi-Unit
CURRADR equ 0x53 ; Current Loco address
CURRSPD equ 0x54 ; Current Loco speed
CURRFNC equ 0x55 ; Current Loco functions
POT_SAVED equ 0x58 ; pot saved values for calc mid value (0x38..0x3F)
;
POT_INDEX equ 0x60 ; Index to POT_SAVE
POT_CNT equ 0x61 ; current pot value
OFFSET equ 0x62 ; pot. offset
SPEED equ 0x63 ; current speed
DIGITH equ 0x64 ; Display. Dizaines
DIGITL equ 0x65 ; Display. Units
SEGMENT equ 0x66 ; segment displayed
ROW equ 0x67 ; Key row scanned
KEYST equ 0x68 ; Keys status
NEWKEY equ 0x69 ; Keys pressed
DEBOUNCE equ 0x6A ; debounce time
MENU equ 0x6B ; current menu
FLASH equ 0x6C ; flashing sequence
; --- Buffers
BUFTXINI equ 0xA0 ; XpressNet Serial output buffer
;----- Flags
#define NEW_TX XFLAGS,0 ; new message to command station
#define TXMIT XFLAGS,1 ; transmiting a message
#define RCVING XFLAGS,2 ; receiving a message
#define NEW_MSG XFLAGS,3 ; received new message to us
#define EmergStop DCC_STATUS,0 ; System in Emergency Stop
#define EmergOff DCC_STATUS,1 ; System in Emergency Off
#define AutoStart DCC_STATUS,2 ; System in auto start-up. Not used
#define ServiceMode DCC_STATUS,3 ; System in Service Mode
#define LOCO_OTHER FLAGS,0 ; current loco is controlled by other device
#define MULTIUNIT FLAGS,2 ; Current is multiunit address
#define SEL_SETUP FLAGS,3 ; Setup selection
#define KeyHit KEYFLAG,0 ; Key hitted
#define ServKey KEYFLAG,1 ; Key need service
#define DbnceOn KEYFLAG,2 ; Key debounce time
#define KeyRep KEYFLAG,3 ; Key repeated
#define TEST_AD KEYFLAG,4 ; Pot change discard
Loco128 equ 7 ; CURRFNC bit definition
Loco28 equ 6
LocoDir equ 5
LocoFL equ 4
LocoF4 equ 3
LocoF3 equ 2
LocoF2 equ 1
LocoF1 equ 0
; --------------- Program Section --------------------------------------
PowerUp:
clrf INTCON ; Disable all interrupts
clrf PCLATH ; Tables on page 0
clrf STATUS ; reset flags
goto INIT
; ----- ISR (Interrupt Service Routines)
movwf INT_W ; 1 Save registers W, STATUS, PCLATH, FSR
swapf STATUS,w ; 2
clrf STATUS ; 3 Ensure port / timer access in bank 0
movwf INT_STAT ; 4
; movf PCLATH,w ;
; movwf INT_PCLATH ; save PCLATH, interrupt uses page 0
; clrf PCLATH ;
movf FSR,w ; Save FSR
movwf INT_FSR
IntRx:
btfss PIR1,RCIF ; serial receive interrupt?
goto IntTx
btfss RCSTA,OERR ; yes, handle RX errors
btfsc RCSTA,FERR
goto IntError
btfss RCSTA,RX9D ; is the call byte?
goto IntRxData ; no
IntRxCallByte:
bcf RCVING ; yes, abort message in progress
clrf RX_LEN
movf RCREG,w ; get call byte and save it
movwf CALLBYTE
btfsc NEW_MSG ; decoded last message?
goto IntRestore ; no, ignore this call byte
xorlw BROADCAST ; broadcast call byte? ('P1100000')
btfsc STATUS,Z
goto IntRxPacket ; yes, new message
movf CALLBYTE,w ; message to us?
xorwf MY_ADR,w ; 'P10AAAAA' anything to transmit?
btfsc STATUS,Z
goto IntRxSend ; yes, answer before 80us
xorlw b'10100000' ; no, 'P11AAAAA' message to us?
btfsc STATUS,Z
goto IntRxPacket ; yes, new message
xorlw b'01100000' ; no, 'P00AAAAA' acknowledge?
btfsc STATUS,Z
goto IntRxAck ; yes, send ack
goto IntRestore ; no, discard
IntRxPacket:
clrf RX_XOR ; set receiving a message
bsf RCVING
goto IntRestore
IntRxData:
movlw BUFRXINI ; save data
addwf RX_LEN,w
movwf FSR
movf RCREG,w ; get data byte to clear interrupt
btfss RCVING ; are we receiving a packet for us?
goto IntRestore ; no, discard data byte
movwf INDF ; yes, save data
xorwf RX_XOR,f ; checksum
incf RX_LEN,f ; next byte
movlw BUFRXINI ; check end of message
movwf FSR
movf INDF,w ; get header byte
andlw 0x0F ; length to receive
addlw 0x02 ; plus header and xor-byte
xorwf RX_LEN,w ; equal to received length?
btfss STATUS,Z
goto IntRestore ; no, receive more bytes
IntRxEnd:
bcf RCVING ; yes, end of receiving data
movf RX_XOR,w
btfsc STATUS,Z ; xor-byte correct?
bsf NEW_MSG ; yes, received new packet
goto IntRestore
IntRxAck: ; Acknowledge transmision (send 0x20, 0x20)
bsf PORTB,0 ; MAX485 TX enable
movlw 0x20
movwf TXREG
banksel TXSTA ;Bank 1
IntTxAck:
btfss TXSTA,TRMT ; wait until end of byte transmited
goto IntTxAck
banksel TXREG ;Bank 0
movlw 0x20 ; send second byte
movwf TXREG
goto IntTxEnd ; wait until transmited
IntRxSend:
btfss NEW_TX ; new message to command station?
goto IntRestore ; no
bsf PORTB,0 ; MAX485 TX enable
banksel PIE1 ;Bank 1
bsf PIE1,TXIE ; Enable TX interrupts
banksel TXPOS ;Bank 0
clrf TXPOS
bsf TXMIT ; now transmiting
goto IntTxByte
IntError:
movf RCREG,w ; on error re-init receiver
bcf RCSTA,CREN
bsf RCSTA,CREN
IntTx:
btfss PIR1,TXIF ; serial transmit interrupt?
goto IntRestore ; no, nothing to do
btfss TXMIT ; yes, still transmiting?
goto IntTxEnd ; no, disable TX interrupts
IntTxByte:
movlw BUFTXINI ; calc buffer position
addwf TXPOS,w
movwf FSR
movf INDF,w ; get byte from buffer
movwf TXREG ; and send it
incf TXPOS,f
decfsz TXCNT,f ; last byte?
goto IntRestore ; no
bcf NEW_TX ; yes, stop transmiting
bcf TXMIT
IntTxEnd:
banksel TXSTA ;Bank 1
IntTxDone:
btfss TXSTA,TRMT ; wait until end of last byte transmited
goto IntTxDone
bcf PIE1,TXIE ; Disable TX interrupts
DNOP
DNOP
DNOP
banksel PORTB ;Bank 0
bcf PORTB,0 ; MAX485 TX disable
IntRestore:
movf INT_FSR,w ; Restore the Context Registers and Return
movwf FSR
; movf INT_PCLATH,w
; movwf PCLATH
swapf INT_STAT,w
movwf STATUS
swapf INT_W,f
swapf INT_W,w
retfie
; ----- Tables on first 256 bytes
BitConvert:
andlw 0x07
addwf PCL,f ; Convert bit position to byte mask
retlw 0x01
retlw 0x02
retlw 0x04
retlw 0x08
retlw 0x10
retlw 0x20
retlw 0x40
retlw 0x80
Digit_RA:
addwf PCL,f ; return bit patterns for each digit
retlw b'10000001' ; '0' a
retlw b'00000000' ; '1' xxxxxx
retlw b'01000001' ; '2' f x x b
retlw b'01000001' ; '3' x g x
retlw b'11000000' ; '4' xxxxxx
retlw b'11000001' ; '5' e x x c
retlw b'11000001' ; '6' x x
retlw b'00000001' ; '7' xxxxxx
retlw b'11000001' ; '8' d
retlw b'11000001' ; '9'
retlw b'01000000' ; '-' bits 76543210
retlw b'00000000' ; '_' segment fgXXXXXa (X = port)
retlw b'00000001' ; '~'
retlw b'11000001' ; 'P'
retlw b'11000001' ; 'E'
retlw b'00000000' ; ' '
retlw b'10000000' ; 'L'
retlw b'01000000' ; 'd'
retlw b'11000001' ; 'A'
Digit_RB:
addwf PCL,f ; return bit patterns for each digit
retlw b'11110000' ; '0' a
retlw b'00110000' ; '1' xxxxxx
retlw b'11010000' ; '2' f x x b
retlw b'01110000' ; '3' x g x
retlw b'00110000' ; '4' xxxxxx
retlw b'01100000' ; '5' e x x c
retlw b'11100000' ; '6' x x
retlw b'00110000' ; '7' xxxxxx
retlw b'11110000' ; '8' d
retlw b'01110000' ; '9'
retlw b'00000000' ; '-' bits 76543210
retlw b'01000000' ; '_' segment edcbXXXX (X = port)
retlw b'00000000' ; '~'
retlw b'10010000' ; 'P'
retlw b'11000000' ; 'E'
retlw b'00000000' ; ' '
retlw b'11000000' ; 'L'
retlw b'11110000' ; 'd'
retlw b'10110000' ; 'A'
GetSegment:
andlw 0x07 ; don't mind digit
addwf PCL,f
goto KeyRow
retlw b'00000001' ; RA a fgXXXX0a bit1: 0:PORTA, 1:PORTB
retlw b'00010010' ; RB b edcbXX1X
retlw b'00100010' ; RB c edcbXX1X
retlw b'01000010' ; RB d edcbXX1X
retlw b'10000010' ; RB e edcbXX1X
retlw b'10000000' ; RA f fgXXXX0a
retlw b'01000000' ; RA g fgXXXX0a
KeyRow:
movf ROW,w ; ROW: 0..3 (4 keys)
addwf PCL,f
retlw b'00000001' ; RA a fgXXXX0a bit1: 0:PORTA, 1:PORTB
retlw b'00010010' ; RB b edcbXX1X
retlw b'10000000' ; RA f fgXXXX0a
retlw b'01000000' ; RA g fgXXXX0a
KeyServ:
bcf ServKey ; Key pressed service routines
btfsc ServiceMode ; discard in Service Mode
return
; btfsc NEWKEY,SEL_KEY
movlw 0x00
btfsc NEWKEY,7 ; F0 key output
movlw 0x01
btfsc NEWKEY,6 ; F1 key output
movlw 0x02
btfsc NEWKEY,4 ;STOP_KEY
movlw 0x03 ; go into current menu options
addwf MENU,w
addwf PCL,f
Menu00Run:
goto K_EnterLok ; Normal Operations
goto K_ChangeDirRun ; K_F1 ***
goto K_Light ; ***
goto K_Stop
Menu04Lok:
goto K_EnterFunc ;Dir ; Change Loco number ***
goto K_LokUp
goto K_LokDwn
goto K_Stop
Menu08Dir:
goto K_EnterRun ; Change Loco Dir/Step
goto K_ChangeDir
goto K_ChangeStep
goto K_Stop
Menu0CSetup:
return ; Setup
goto K_SelSetup
goto K_EnterPar
goto INIT
Menu10Xbus:
goto K_SelSetup ; Change Xbus address
goto K_XbusUp
goto K_XbusDwn
goto INIT
Menu14Pot:
goto K_SelSetup ; Change pot offset
goto K_OffsetUp
goto K_OffsetDwn
goto INIT
Menu18Fnc:
goto K_EnterDir ; Additional functions
goto K_F1
goto K_F2
goto K_Stop
if ($ > 255)
ERROR " Tables exceded page 0. If it works, why do you change it? "
endif
; ----- Initialization
INIT:
clrf RCSTA ; disable UART
movlw 0x18 ; display off, receive
movwf PORTA
movlw 0x00 ; all zero
movwf PORTB
banksel TRISB ;Bank 1
movlw 0x06 ; all outputs except serial port
movwf TRISB
movlw 0x26 ; RA1,RA2: comparator input,RA5: key input
movwf TRISA
movlw 0x20 ; RX interrupt
movwf PIE1 ; peripheral interrupts
movlw 0x81 ; Option reg: timer prescaler 1:4, no pull-ups
; Port B Pull-up: 1 disabled
; External int edge: 0 falling
; Timer trigger source: 0 clock
; Timer trigger edge: 0 rising
; Prescaler assignment: 0 Timer0
; Prescaler value: 001 1:4
movwf OPTION_REG
movlw 0x0B ; internal oscillator to 4MHz default
movwf PCON
movlw 0x00 ; VRR off
movwf VRCON
banksel CMCON ;Bank 0
movlw 0x05 ; RA1, RA2 analog inputs
movwf CMCON
movlw 0x31 ; Timer 1. Run, set 1:8 Prescaler
movwf T1CON
movlw 0x04 ; Timer 2. Run with 1:1 Prescaler
movwf T2CON
movlw 0x20 ; Clear variables bank0
movwf FSR
ClearRAM:
clrf INDF
incf FSR,f
btfss FSR,7
goto ClearRAM
call SetMyAddress ; get Xbus address
call UART_INI ; Init peripheals
bcf PORTB,0 ; MAX485 TX disable just to be sure
call IniPWM ;
movlw 0xC0 ; GIE enable, PEIE enable
movwf INTCON
; bsf PORTA,SEL_KEY ; Enter setup mode if SEL pressed
movlw 0x19
movwf PORTA
DNOP
DNOP
movlw 0x0A ; Diplay '--'
movwf DIGITH
movwf DIGITL
movlw 0xFF ; no flash
movwf FLASH
DNOP
DNOP
btfsc PORTA,5 ; Key input
goto Setup
movlw LOK_MEM ; get last loco used
call EE_Read
movwf CURRADR
movlw 0x00 ; stop default
movwf CURRSPD
movlw b'01100000' ; default 28 steps, forward, FL,F1..F4 off
movwf CURRFNC
call GetVersion ; get command station version & type
clrf TMR1L ; time to transmit to command station
clrf TMR1H
bcf PIR1,TMR1IF
FindStation:
btfsc INTCON,T0IF ; update display and keyboard
call TimerOut
btfsc NEW_MSG ; new message received?
call HandleMsg ; yes, handle it
btfsc PIR1,TMR1IF
call NotFound ; timeout
btfsc NEW_TX ; packet sended, command station present
goto FindStation
; bsf T1CON,TMR1ON ; Timer1 on to be sure
call GetStatus ; get current status
movlw 0x01 ; Timer 1. Run, set 1:1 Prescaler
movwf T1CON
clrf TMR1L ; time to transmit changes to command station
clrf TMR1H ; and read keys and pot value
bcf PIR1,TMR1IF
MainLoop:
call K_EnterRun ; show last loco used and get info
call GetLocoInfo
bsf TEST_AD
movlw 0x10
movwf MSB
MainStabilize:
btfsc INTCON,T0IF ; update display and keyboard
call TimerOut
btfsc NEW_MSG ; new message received?
call HandleMsg ; yes, handle it
btfss PIR1,TMR1IF ; wait stabilize time 1 sec
goto MainStabilize
bcf PIR1,TMR1IF
decfsz MSB,f
goto MainStabilize
movlw 0xF0 ; clear keys status
andwf KEYFLAG,f
clrf KEYST
Loop:
btfsc INTCON,T0IF ; update display and keyboard
call TimerOut
btfsc NEW_MSG ; new message received?
call HandleMsg ; yes, handle it
btfsc PIR1,TMR1IF ; read pot value if needed
call FlashPotSpeed
btfsc ServKey ; key need service?
call KeyServ ; yes, do action
goto Loop
; --- No Command Station avaiable
NotFound:
bcf PIR1,TMR1IF
movlw CHR_E ; show error 'E7'. SimpleMaus not addressed
movwf DIGITH
movlw 0x07
movwf DIGITL
return
; --- Setup Loop
Setup:
call K_EnterSetup ; Select Menu setup
movlw 0x01 ; Timer 1. Run, set 1:1 Prescaler
movwf T1CON
SetupLoop:
btfsc PIR1,TMR1IF ; show pot value if needed
call ShowPot
btfsc INTCON,T0IF ; update display and keyboard
call TimerOut
btfsc ServKey ; key need service?
call KeyServ ; yes, do action
goto SetupLoop
; --- Xbus Incoming messages
HandleMsg: ; Handle incoming messages
Handle6X:
movf HEADER,w
Handle61:
xorlw 0x61
btfss STATUS,Z
goto Handle6X_2
Handle61_0:
movf DATA1,w
btfss STATUS,Z
goto Handle61_1
btfsc EmergOff ; second transmision
goto HandleEnd
bcf ServiceMode ; Track power off
bsf EmergOff
bcf EmergStop
HandleOff:
movlw CHR_E ; display 'E0'
movwf DIGITH
movlw 0x00
movwf DIGITL
call SetFlash
goto HandleHomeEnd
Handle61_1:
xorlw 0x01
btfss STATUS,Z
goto Handle61_2
bcf ServiceMode ; normal operations resume
bcf EmergOff
bcf EmergStop
HandleResume:
movf CURRADR,w ; show current loco
call DispBin
call SetFlash
movf DCC_STATUS,w ; second transmision?
andlw b'00001011'
btfsc STATUS,Z
goto HandleHomeEnd ; yes, prevent hanging
bcf NEW_MSG ; message processed
call GetLocoInfo ; get loco info
goto HandleHomeEnd
Handle61_2:
xorlw (0x01 ^ 0x02)
btfss STATUS,Z
goto HandleEnd
btfsc ServiceMode ; second transmision
goto HandleEnd
bsf ServiceMode ; Service Mode entry
bcf EmergOff
bcf EmergStop
HandleServ:
movlw 0x05 ; display 'SP'
movwf DIGITH
movlw CHR_P
movwf DIGITL
call SetFlash
goto HandleHomeEnd
Handle6X_2:
xorlw (0x61 ^ 0x62)
btfss STATUS,Z
goto Handle6X_3
Handle62:
movf DATA1,w
xorlw 0x22
btfss STATUS,Z
goto HandleEnd
movf DATA2,w ; Commnad station status response
movwf DCC_STATUS
btfsc ServiceMode ; set correct status
goto HandleServ
btfsc EmergOff
goto HandleOff
btfsc EmergStop
goto HandleStop
goto HandleResume
Handle6X_3:
xorlw (0x62 ^ 0x63)
btfss STATUS,Z
goto Handle8X
Handle63:
movf DATA1,w
xorlw 0x21
btfss STATUS,Z
goto HandleEnd
movf DATA2,w ; software version
movwf XBUS_VER
movf DATA3,w
movwf XBUS_STA
goto HandleEnd
Handle8X:
movf HEADER,w
Handle81:
xorlw 0x81
btfss STATUS,Z
goto HandleEX
movf DATA1,w
btfss STATUS,Z
goto HandleEnd
btfsc EmergStop ; second transmision
goto HandleEnd
bcf ServiceMode ; Emergency stop
bcf EmergOff
bsf EmergStop
HandleStop:
movlw CHR_E ; show error 'ES'. Emergency stop
movwf DIGITH
movlw 0x05
movwf DIGITL
call SetFlash
goto HandleHomeEnd
HandleEX:
movf HEADER,w
HandleEX_1:
xorlw 0xE1 ; MU+DH errors
btfss STATUS,Z
goto HandleEX_2
HandleE1:
movf DATA1,w
xorlw 0x88 ; stack full
btfss STATUS,Z
goto HandleEnd
HandleFull:
movlw 0x05 ; show error 'S-'. Stack full
movwf DIGITH
movlw 0x0A
movwf DIGITL
movlw b'11001100' ; fast flashing
movwf FLASH
goto HandleEnd
HandleEX_2:
xorlw (0xE1 ^ 0xE2)
btfss STATUS,Z
goto HandleEX_3
HandleE2:
movf DATA1,w ; Loco information
andlw 0xF0
xorlw 0x20
btfss STATUS,Z
goto HandleEnd
bsf MULTIUNIT ; set Multi Unit
movf CURRADR,w ; no functions
movwf CURRMTR
clrf CURRFNC
goto HandleE4_Data
HandleEX_3:
xorlw (0xE2 ^ 0xE3) ; Loco is being operated by another device
btfss STATUS,Z
goto HandleEX_4
movf DATA1,w ; Loco information
xorlw 0x40
btfss STATUS,Z
goto HandleEnd
movf DATA2,w ; check with CURRADR
andlw 0x3F
btfss STATUS,Z
goto HandleEnd
movf DATA3,w
xorwf CURRADR,w
btfss STATUS,Z
goto HandleEnd
bsf LOCO_OTHER ; operated by other
call SetFlash
bsf TEST_AD
goto HandleEnd
HandleEX_4:
xorlw (0xE3 ^ 0xE4)
btfss STATUS,Z
goto HandleEX_5
HandleE4_0:
movf DATA1,w ; Loco information normal locomotive
andlw 0xF0
btfss STATUS,Z
goto HandleEnd
bcf MULTIUNIT
movf CURRADR,w ; normal locomotive
movwf CURRMTR
HandleE4_Loco:
movf DATA3,w ; function
andlw 0x1F ; clear other bits to be sure
movwf CURRFNC
HandleE4_Data:
btfsc DATA2,7 ; set dir
bsf CURRFNC,LocoDir
btfss DATA1,0 ; set 27 -> steps
btfsc DATA1,1 ; set 28 steps
bsf CURRFNC,Loco28
btfsc DATA1,2 ; set 128 steps
bsf CURRFNC,Loco128
movf DATA2,w
andlw 0x7F
movwf CURRSPD
bcf LOCO_OTHER ; loco controlled by other device?
btfsc DATA1,3
bsf LOCO_OTHER
call SetFlash
bsf TEST_AD
goto HandleEnd
HandleEX_5:
xorlw (0xE4 ^ 0xE5)
btfss STATUS,Z
goto HandleEnd
HandleE5_1:
movf DATA1,w ; Loco information in multi-unit
andlw 0xF0
xorlw 0x10
btfss STATUS,Z
goto HandleEnd
bsf MULTIUNIT
movf DATA5,w ; Loco in a Multi Unit
movwf CURRMTR
goto HandleE4_Loco
HandleHomeEnd:
movlw MENU_RUN
movwf MENU
call SetFlash
HandleEnd:
bcf NEW_MSG ; message processed
return
; --- Request messages
GetVersion:
movlw 0x21 ; get software version request
call SendHeader
movlw 0x21
call SendData
goto SendMsg
GetStatus:
movlw 0x21 ; get status request
call SendHeader
movlw 0x24
call SendData
goto SendMsg
Estop:
movlw 0x80 ; Emergency stop
call SendHeader
goto SendMsg
EOff:
movlw 0x21 ; Emergency Off
call SendHeader
movlw 0x80
call SendData
goto SendMsg
Resume:
movlw 0x21 ; Resume operations request
call SendHeader
movlw 0x81
call SendData
goto SendMsg
GetLocoInfo:
movlw 0xE3 ; Locomotive information request
call SendHeader
movlw 0x00
call SendData
movlw 0x00 ; AH
call SendData
movf CURRADR,w ; AL
call SendData
goto SendMsg
LocoOperations:
movlw 0xE4 ; Locomotive operations request
call SendHeader
movlw 0x10 ; 14 steps
btfsc CURRFNC,Loco28
movlw 0x12 ; 28 steps
btfsc CURRFNC,Loco128
movlw 0x13 ; 128 steps
call SendData
movlw 0x00 ; AH
call SendData
movf CURRADR,w ; AL
call SendData
movf CURRSPD,w
btfsc CURRFNC,LocoDir
iorlw 0x80
call SendData
SaveCurrentLoco:
bcf LOCO_OTHER
call SetFlash
movf CURRADR,w ; save current used loco
movwf EEDATA0
movlw LOK_MEM
call SetParm
goto SendMsg
FuncOperations:
movlw 0xE4 ; Function operations request
call SendHeader
movlw 0x20
call SendData
movlw 0x00 ; AH
call SendData
movf CURRADR,w ; AL
call SendData
movf CURRFNC,w ; Function group 1
andlw 0x1F
call SendData
goto SaveCurrentLoco
; --- XpressNet messages routines
SendHeader:
movwf DUMMY
SendHeaderW:
btfsc NEW_MSG ; new message received?
call HandleMsg ; yes, handle it
btfsc NEW_TX ; last message sent?
goto SendHeaderW ; no, wait
clrf TXPOS ; xor byte
clrf TXCNT ; byte count
movf DUMMY,w
SendData:
movwf TEMP
movlw BUFTXINI
addwf TXCNT,w
movwf FSR
movf TEMP,w
movwf INDF
xorwf TXPOS,f
incf TXCNT,f
return
SendMsg:
movf TXPOS,w ; Xor byte
call SendData
bsf NEW_TX ; transmit this
return
SetMyAddress:
movlw ADR_MEM ; read from EEPROM
call EE_Read
andlw 0x1F ; address beetwen 1 to 31
iorlw 0x40 ; anything to transmit default value
movwf MY_ADR
movwf TEMP ; 0000 0011
swapf TEMP,W ; 0011 0000
xorwf TEMP,F ; 0011 0011
rrf TEMP,W ; X001 1001 1
xorwf TEMP,F ; X010 1010
btfsc TEMP,2
incf TEMP, F
rrf TEMP,w ; XX01 0101 0 ; C flag = parity (1=odd)
btfsc STATUS,C
bsf MY_ADR,7
return
; --- Display / Keyboard scanning
TimerOut:
bcf INTCON,T0IF ; SEGMENT=0 -> ROW
call ReadPot ; every 1 ms
; bsf PORTA,DISPH ; display off
; bsf PORTA,DISPL
movlw 0x18 ; display off
movwf PORTA
incf SEGMENT,f ; Segment: 0..7, bit 3: Display selection
movf SEGMENT,w
andlw 0x07
btfsc STATUS,Z
goto ReadKeyb
movf DIGITL,w ; select digit to show
btfsc SEGMENT,3
movf DIGITH,w
call Display ; light one segment
btfss FLASH,7 ; flashing?
return
btfss SEGMENT,3 ; display on
bcf PORTA,4 ; Display low enable
btfsc SEGMENT,3
bcf PORTA,3 ; Display high enable
return
ReadKeyb:
btfss DbnceOn ; debounce time?
goto Scan ; no, scan keyboard
decfsz DEBOUNCE,f ; yes, wait debouncing
return
bcf DbnceOn ; end debounce
movlw 120 ; first time auto repeat
movwf DEBOUNCE
return
Scan:
incf ROW,w
andlw 0x03
movwf ROW
movlw 0x08 ; all active rows
call Display
DNOP
DNOP
DNOP
DNOP
movf TEMP2,w ; segment bit
andlw b'11010001'
movwf TEMP2
comf TEMP2,w ; segment bit
andwf KEYST,f ; clear bit
movf TEMP2,w
btfsc PORTA,5 ; Key input
iorwf KEYST,f ; set if pressed
movf KEYST,w
btfsc STATUS,Z ; key pressed?
goto NoKey ; no
btfsc KeyHit ; yes, last key released?
goto KeyRepeat ; no
bsf KeyHit
bcf KeyRep
movf KEYST,w
movwf NEWKEY
bsf DbnceOn
movlw 8
movwf DEBOUNCE
KeyReturn:
bsf ServKey ; key need service
return
NoKey:
movf ROW,w ; all buttons readed?
btfss STATUS,Z
return ; no, wait
bcf KeyHit ; set no key pressed
bcf KeyRep
return
KeyRepeat:
decfsz DEBOUNCE,f ; wait auto repeat time
return
bsf KeyRep
movlw 16 ; next time auto repeat
movwf DEBOUNCE
goto KeyReturn ; set key pressed
Display:
movwf TEMP ; digit to display
movf SEGMENT,w
call GetSegment
movwf TEMP2
bcf INTCON,GIE ; disable interrupts
movlw b'00111110' ; display off fgXXXXXa
andwf PORTA,f
movlw b'00001111' ; edcbXXXX
andwf PORTB,f
movf TEMP,w
btfss TEMP2,1 ; select port
goto DisplayRA
DisplayRB:
call Digit_RB
andwf TEMP2,w ; segment
iorwf PORTB,f
bsf INTCON,GIE
return
DisplayRA:
call Digit_RA
andwf TEMP2,w ; segment
iorwf PORTA,f
bsf INTCON,GIE
return
; ---- Display
DispBin:
movwf LSB ; Show bin: 0..99
call BIN2BCD
movf R0,w
DispBCD:
movwf TEMP ; out 2 BCD digits
swapf TEMP,w
andlw 0x0F ; out BCD tens
movwf DIGITH
movf TEMP,w
DispBCDL:
andlw 0x0F ; out BCD units
movwf DIGITL
return
SetFlash:
movlw b'11111111' ; always on
movf MENU,f
btfss STATUS,Z
movlw b'11011111' ; minimum flashing
btfsc LOCO_OTHER
movlw b'11001100' ; fast flashing
btfsc EmergOff
movlw b'11000011' ; slow flashing
btfsc EmergStop
movlw b'11000011' ; slow flashing
btfsc ServiceMode
movlw b'11000011' ; slow flashing
movwf FLASH
return
BIN2BCD:
bcf STATUS,C ; 8 bit bin to 2(3) digit BCD
movlw 0x08
movwf COUNT
clrf R0
clrf R1
LOOP8:
rlf LSB,f
rlf R0,f
; rlf R1,f
decfsz COUNT,f
goto ADJDEC
return
ADJDEC:
movlw R0
call ADJBCD
; movlw R1
; call ADJBCD
goto LOOP8
ADJBCD:
movwf FSR
movlw 0x3
addwf INDF,W
movwf TEMP
btfsc TEMP,3
movwf INDF
movlw 0x30
addwf INDF,W
movwf TEMP
btfsc TEMP,7
movwf INDF
return
; ----- Menu Keys
K_EnterLok:
movlw CHR_L ; Menu Loco selection. Diplay 'L '
movwf DIGITH
movlw CHR_SPC
movwf DIGITL
movlw MENU_LOK
movwf MENU
call SetFlash
return
K_LokUp:
incf CURRADR,f ; Increment Loco number
movlw 100
xorwf CURRADR,w
movlw 1
btfsc STATUS,Z
movwf CURRADR
K_LokEnd:
call GetLocoInfo
movf CURRADR,w
goto DispBin
K_LokDwn:
decf CURRADR,f ; Decrement Loco number
movlw 99
btfsc STATUS,Z
movwf CURRADR
goto K_LokEnd
K_Light:
movlw 0x10 ; Change FL ***
xorwf CURRFNC,f ; Change Fx
goto FuncOperations
K_F2:
movlw 0x02 ; Change F2 ***
goto K_Func
K_F1:
movlw 0x01 ; Change F1
goto K_Func
K_Func:
xorwf CURRFNC,f ; Change Fx
call FuncOperations
K_EnterFunc:
movlw 0x0B ; '_' F2 status ***
btfsc CURRFNC,1
movlw 0x0C ; '~'
movwf DIGITL
movlw 0x0B ; '_' F1 status
btfsc CURRFNC,0
movlw 0x0C ; '~'
movwf DIGITH
movlw MENU_FNC
movwf MENU
call SetFlash
return
K_ChangeDir:
; movf SPEED,w ; Change Direction only in speed 0 ***
; btfss STATUS,Z
; return
movlw 0x20 ; Change current loco direction
xorwf CURRFNC,f
call LocoOperations
K_EnterDir:
movf SPEED,w ; Change Dir / steps only in speed 0
btfss STATUS,Z
goto K_EnterRun
btfss CURRFNC,LocoDir ; Menu Direction/Steps.
goto K_EnterDirBack
; Forward 'd-'
movlw CHR_D ; 'd'
movwf DIGITH
movlw 0x0B ; '_' 14 steps
btfsc CURRFNC,Loco28
movlw 0x0A ; '-' 28 steps
btfsc CURRFNC,Loco128
movlw 0x0C ; '~' 128 steps
movwf DIGITL
goto K_EnterDirEnd
K_EnterDirBack: ; Backward '-d'
movlw 0x0B ; '_' 14 steps
btfsc CURRFNC,Loco28
movlw 0x0A ; '-' 28 steps
btfsc CURRFNC,Loco128
movlw 0x0C ; '~' 128 steps
movwf DIGITH
movlw CHR_D ; 'd'
movwf DIGITL
K_EnterDirEnd:
movlw MENU_DIR
movwf MENU
call SetFlash
return
K_ChangeDirRun:
movlw 0x20 ; Change current loco direction ***
xorwf CURRFNC,f
call LocoOperations
K_EnterRun:
movlw MENU_RUN ; Main menu, show loco number
movwf MENU
call SetFlash
movf CURRADR,w
goto DispBin
K_ChangeStep:
movf SPEED,w ; only if speed 0
btfss STATUS,Z
return
movlw b'10000000' ; change step 14,28,128
btfss CURRFNC,Loco128
movlw b'01000000'
btfsc CURRFNC,Loco28
movlw b'11000000'
xorwf CURRFNC,f
call LocoOperations ; update new steps
goto K_EnterDir
K_Stop:
btfsc EmergStop ; in E_stop or E_Off?
K_StopOn:
goto Resume ; yes, Resume
K_StopOff:
btfsc EmergOff
goto K_StopOn
goto EOff ; no, do Emergency Off
K_XbusUp:
movlw ADR_MEM ; Increment Xbus address
call EE_Read
movwf EEDATA0
incf EEDATA0,f
movlw 32
xorwf EEDATA0,w
movlw 1
btfsc STATUS,Z
movwf EEDATA0
K_XbusUpdate:
movlw ADR_MEM
call SetParm
movf EEDATA0,w
goto DispBin
K_XbusDwn:
movlw ADR_MEM ; Decrement Xbus address
call EE_Read
movwf EEDATA0
decf EEDATA0,f
movlw 31
btfsc STATUS,Z
movwf EEDATA0
goto K_XbusUpdate
K_SelSetup:
btfss SEL_SETUP ; Change setup menu selection
goto K_EnterSetupPot
K_EnterSetup:
bcf SEL_SETUP
movlw CHR_A ; show 'A '
goto K_EnterSetupEnd
K_EnterSetupPot:
bsf SEL_SETUP
movlw CHR_P ; show 'P '
K_EnterSetupEnd:
movwf DIGITH
movlw CHR_SPC
movwf DIGITL
movlw MENU_SETUP
movwf MENU
return
K_EnterPar:
movlw MENU_POT ; Set selected setup menu
btfss SEL_SETUP
movlw MENU_XBUS
movwf MENU
btfsc SEL_SETUP
return
movlw ADR_MEM ; Show current Xbus address
call EE_Read
goto DispBin
K_OffsetUp:
movlw POT_MEM ; Increment Pot offset
call EE_Read
movwf EEDATA0
incf EEDATA0,f ; 0..15
btfsc EEDATA0,4
decf EEDATA0,f
K_OffsetUpdate:
movf EEDATA0,w ; set new offset
movwf OFFSET
movlw POT_MEM
goto SetParm
K_OffsetDwn:
movlw POT_MEM ; Decrement Pot offset
call EE_Read
movwf EEDATA0
decf EEDATA0,f ; 0..15
btfsc EEDATA0,7
clrf EEDATA0
goto K_OffsetUpdate
; ----- Analog
IniPWM:
bsf STATUS,RP0
movlw 51 ; Set PWM freq = 19200 Hz (R:2K7, C:100nF) ??
movwf PR2
bcf STATUS,RP0
movlw 0x04 ; enable TMR2 1:1 prescaler
movwf T2CON
movlw POT_MEM ; read pot. offset
call EE_Read
andlw 0x0F
movwf OFFSET
movlw 0x1F ; init pot counter
movwf POT_CNT
addwf OFFSET,w
movwf CCPR1L ; analog output max. voltage (5bits + 2bits)
movlw 0x0C ; enable PWM mode
movwf CCP1CON
return
; copy POT_CNT to next POT_SAVED array
ReadPot:
movf POT_INDEX,w
andlw 0x07
addlw POT_SAVED
movwf FSR
btfss CMCON,C2OUT ; check if decreased to pot position
goto ReadPotNxt
movf POT_CNT,w ; yes, get position
movwf INDF ; Speed = PWM - Offset (0..31)
incf POT_INDEX,f
goto ReadPotEnd
ReadPotNxt:
decf CCPR1L,f
decfsz POT_CNT,f ; next step voltage
return
clrf INDF ; pot in stop
incf POT_INDEX,f
ReadPotEnd:
movlw 0x1F ; reload max. voltage
movwf POT_CNT
addwf OFFSET,w ; pot. offset
movwf CCPR1L
return
;Read potentiometer value from RAM and — if changed — send it to loco
FlashPotSpeed:
bcf PIR1,TMR1IF ; calc flashing sequence
bcf STATUS,C
btfsc FLASH,7
bsf STATUS,C
rlf FLASH,f
;PotSpeed:
btfsc NEW_TX ; don't read if waiting to send
return
movf POT_SAVED,w ; calc medium value
addwf POT_SAVED+1,w
addwf POT_SAVED+2,w
addwf POT_SAVED+3,w
addwf POT_SAVED+4,w
addwf POT_SAVED+5,w
addwf POT_SAVED+6,w
addwf POT_SAVED+7,w
movwf TEMP
rrf TEMP,f
rrf TEMP,f
rrf TEMP,w
andlw 0x1F
movwf TEMP
xorwf SPEED,w ; pot equal to last position?
btfsc STATUS,Z
return ; yes, nothing to do
movf TEMP,w ; save new position
movwf SPEED
btfsc TEST_AD
goto PotDiscard
btfsc CURRFNC,Loco28 ; convert to current speed step
goto PotSpd28
btfsc CURRFNC,Loco128
goto PotSpd128
;PotSpd14:
rrf TEMP,w ; 14 steps
andlw 0x0F
xorlw 0x01 ; skip E.stop
btfss STATUS,Z
xorlw 0x01
movwf CURRSPD ; update speed
goto LocoOperations
PotSpd28:
movlw 0x04 ; skip emergency & stop
subwf TEMP,w
btfss STATUS,C
clrf TEMP
rrf TEMP,w ; set bit 4 as LSB
btfsc STATUS,C
iorlw 0x10
andlw 0x1F
movwf CURRSPD ; update speed
goto LocoOperations
PotSpd128:
rlf TEMP,f ; SPEED * 4 (0..127)
rlf TEMP,w
andlw 0x7C
movwf CURRSPD ; update speed
goto LocoOperations
PotDiscard:
bcf TEST_AD
return
ShowPot:
bcf PIR1,TMR1IF
movf MENU,w ; show only in Setup Pot. Menu
xorlw MENU_POT
btfss STATUS,Z
return
movf POT_SAVED,w ; calc medium value
addwf POT_SAVED+1,w
addwf POT_SAVED+2,w
addwf POT_SAVED+3,w
addwf POT_SAVED+4,w
addwf POT_SAVED+5,w
addwf POT_SAVED+6,w
addwf POT_SAVED+7,w
movwf TEMP
rrf TEMP,f
rrf TEMP,f
rrf TEMP,w
andlw 0x1F
goto DispBin
; ----- Serial port routines (XBus)
UART_INI:
bcf PORTB,0 ; Disable TX
clrf XFLAGS ; clear XpressNet flags
clrf RX_LEN ; clear input buffer
banksel SPBRG ;Bank 1
movlw ((10*FXTAL/(16*BaudRate))+5)/10-1 ; BRGH = 1
movwf SPBRG
movlw 0x64 ; 9 bit, Enable TX, BRGH=1, TXD9=0
movwf TXSTA
banksel RCSTA ;Bank 0
movlw 0xD0 ; Enable RX, 9 bit
movwf RCSTA
clrw ; Settling time
SETTLE:
addlw 0xFF
btfss STATUS,Z
goto SETTLE
movf RCREG,w ; flush receive buffer
movf RCREG,w
movf RCREG,w
return
;----- Internal EEPROM routines ------------------------------------------------
EE_Read:
bsf STATUS,RP0 ; w=ADR
movwf EEADR
bsf EECON1,RD
movf EEDATA,w
bcf STATUS,RP0
return
SetParm:
call EE_Read ; w=ADR, EEDATA0=data. Write only changes
xorwf EEDATA0,w
btfsc STATUS,Z
return
EE_Write:
movf EEDATA0,w
bsf STATUS,RP0
movwf EEDATA
bsf EECON1,WREN
bcf INTCON,GIE
movlw 0x55
movwf EECON2
movlw 0xAA
movwf EECON2
bsf EECON1,WR
bsf INTCON,GIE
bcf EECON1,WREN
EEWrite0:
btfsc EECON1,WR
goto EEWrite0
bcf STATUS,RP0
return
; ----- EEPROM default values
org 0x2100
dw 0x01 ; XpressNet device address
dw 0x03 ; Last Locomotive used
dw 0x00 ; Pot. offset
org 0x2110
dt " Simple "
dt "Maus v."
dt (__VERNUM & 0x0F)+0x30
dt "F.Cañada"
dt (__VERDAY >> 4) +0x30
dt (__VERDAY & 0x0F)+0x30,"/"
dt (__VERMONTH >> 4) +0x30
dt (__VERMONTH & 0x0F)+0x30,"/"
dt (__VERYEAR >> 4) +0x30
dt (__VERYEAR & 0x0F)+0x30
end
Detected encoding: UTF-8 | 0
|