;======================================================================
; SimpleMaus
;======================================================================;;
; Program: SimpleMaus -- Lokmaus control
; Orig. Code: Paco Cañada ( http://www.fut.es/~fmco )
; Platform: Microchip PIC16F628, 4 MHz ≙ 1 MIPS
;;======================================================================;;
;Tab width = 8, EOL = LF, Charset = UTF-8
; This program is for a simple LokMaus control (Lenz XBus).
; It uses two LED 7-segment displays common cathode with one common resistor each.
; Uses PWM output for DAC that is compared with potentiometer voltage
; in 100 steps (0..99) (= DAC following ADC principle)
; 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.
;190120 heha source code revision with equal hex file
; Hardware: Four keys, one potentiometer,
; two 7-segment displays (dp not used), one RS485 (MAX485) transceiver
;RA0 17 Key "Sel", Segment "a"
;RA1 18 AN1 lowpass filtered PWM to analog comparator 2 (-) *
;RA2 1 AN2 potentiometer wiper to analog comparator 2 (+)
;RA3 2 First digit CCA; +NEW Segment "dp" of second digit
;RA4 3 Second digit CCA; +NEW Segment "dp" of first digit
;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 drive signal, H=send, L=receive
;RB1 7 RX MAX485 Receive
;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"
;CCA means Common Cathode or Anode, will be detected by firmware automatically
;* = Free pins for PIC16F1826 as that chip has an A/D converter
;Use of timers:
;Timer0:
;Timer1: General timeout generator
;Timer2: PWM generation for potentiometer readback
list p=16F628,r=DEC,n=0,st=off
noexpand
include "P16F628.INC"
assume 0
__FUSES _BODEN_ON & _CP_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLRE_OFF & _INTRC_OSC_NOCLKOUT
; ----- Macros
DNOP macro
goto $+1
endm
movfwf macro s,d
movfw s
movwf d
endm
movlwf macro l,d
movlw l
movwf d
endm
BANK0 macro ; Built-in BANKSEL macro is stupid and generates 2 instructions
bcf STATUS,RP0 ; As this program uses only Bank 0 and 1, the RP1 bit is always zero.
endm
BANK1 macro
bsf STATUS,RP0
endm
; ----- Constant values
FXTAL equ 4000000 ; Internal oscillator frequency
BaudRate equ 62500 ; XBus serial speed
; --- Entry mode constants, defines what will be shown on display. Pressing a key will branch to one of four subitems.
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
;direct 7segment codes; a space is simply zero
_0 equ b'0111111' ; a
_1 equ b'0000110' ; xxxxxx
_2 equ b'1011011' ; f x x b
_3 equ b'1001111' ; x g x
_4 equ b'1100110' ; xxxxxx
_5 equ b'1101101' ; e x x c
_6 equ b'1111101' ; x x
_7 equ b'0000111' ; xxxxxx
_8 equ b'1111111' ; d
_9 equ b'1101111'
_A equ b'1110111'
_d equ b'1011110'
_E equ b'1111001'
_L equ b'0111000'
_P equ b'1110011'
_UP equ b'0000001' ;upper bar, means maximum value or “on”
_MI equ b'1000000' ;middle bar; minus sign, means middle value
_DN equ b'0001000' ;lower bar; down; underline, means minimum value or “off”
; --- EEPROM addresses
ADR_MEM equ 0x00 ; Xbus address EEPROM location
LOK_MEM equ 0x01 ; Last loco used
POT_MEM equ 0x02 ; Potentiometer offset
; --- Non-banked variables
SaveW equ 0x70 ; Interrupt variables.
SaveSTATUS equ 0x71
;SavePCLATH equ 0x72
SaveFSR equ 0x73
EEDATA0 equ 0x7B ; EEPROM access related variables
EEADR0 equ 0x7C
; --- Bank 0
cblock 0x20
HEADER ; first input buffer location = Xbus packet
DATA1
DATA2
DATA3
DATA4
DATA5
endc
KEYFLAG equ 0x35 ; Keyboard flags
cblock 0x38
potValue
R1
LSB
MSB
DUMMY ; Temporary values
TEMP
TEMP2
COUNT
CALLBYTE ; call byte byte from command station
MY_ADR ; XpressNet Address for normal inquiry messages
TXPOS ; Serial buffer index / xorbyte
TXCNT ; Serial Buffer output index
RX_XOR ; Serial incoming checksum
RX_LEN ; Incomming message length
XFLAGS ; XpressNet flags
XBUS_VER ; XpressNet version
XBUS_STA ; Command Station type
FLAGS ; Flags
endc
DCC_STATUS equ 0x4F ; Command Station Status
potFiltL equ 0x50
potFiltH equ 0x51 ;256 Werte aufsummieren und dann abliefern
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
potPWM equ 0x56 ;current PWM calue, avoids readback of CCPR1L
potFiltN equ 0x57
cblock 0x58
;POT_SAVED:8 ; saved A/D values for calc mean value
;POT_INDEX ; Index to POT_SAVE
;POT_CNT ; current pot value
OFFSET ; pot. offset
SPEED ; current speed
digitH ; Left digit
digitL ; Right digit
segMask ; current segment shown, only one bit is set at any time
keyCollect ; key bits to collect while complete multiplexing cycle of 7 ms
keyState ; Keys status, -FF---SS
keyNew ; Keys that changed status from released to pressed
keyRepeat ; debounce time
MENU ; current menu
FLASH ; flashing sequence
endc
; --- Buffers
BUFTXINI equ 0xA0 ; XpressNet Serial output buffer in Bank 1
;----- Flags
#define xfNewTx XFLAGS,0 ; new message to command station
#define xfXmit XFLAGS,1 ; transmiting a message
#define xfRcv XFLAGS,2 ; receiving a message
#define xfNewMsg 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 flLocoOther FLAGS,0 ; current loco is controlled by other device
#define flMultiUnit FLAGS,2 ; Current is multiunit address
#define flSelSetup FLAGS,3 ; Setup selection, either "A " or "P "
#define flPotValue FLAGS,5 ; have new potentiometer value (each 102.4 ms)
;#define KeyHit KEYFLAG,0 ; Key hit detected
;#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
; --------------- 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 SaveW ; 1 Save registers W, STATUS, PCLATH, FSR
swapf STATUS,w ; 2
clrf STATUS ; 3 Ensure port / timer access in bank 0
movwf SaveSTATUS ; 4
; movfwf PCLATH,SavePCLATH ; save PCLATH, interrupt uses page 0
; clrf PCLATH
movfwf FSR,SaveFSR ; Save 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 xfRcv ; yes, abort message in progress
clrf RX_LEN
movfwf RCREG,CALLBYTE ; get call byte and save it
btfsc xfNewMsg ; decoded last message?
goto IntRestore ; no, ignore this call byte
xorlw 0x60 ; broadcast call byte? ('P1100000')
bz IntRxPacket ; yes, new message
movfw CALLBYTE ; message to us?
xorwf MY_ADR,w ; 'P10AAAAA' anything to transmit?
bz IntRxSend ; yes, answer before 80us
xorlw b'10100000' ; no, 'P11AAAAA' message to us?
bz IntRxPacket ; yes, new message
xorlw b'01100000' ; no, 'P00AAAAA' acknowledge?
bz IntRxAck ; yes, send ack
goto IntRestore ; no, discard
IntRxPacket:
clrf RX_XOR ; set receiving a message
bsf xfRcv
goto IntRestore
IntRxData:
movlw HEADER ; save data
addwf RX_LEN,w
movwf FSR
movfw RCREG ; get data byte to clear interrupt
btfss xfRcv ; 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
movlwf HEADER,FSR ; check end of message
movfw INDF ; get header byte
andlw 0x0F ; length to receive
addlw 0x02 ; plus header and xor-byte
xorwf RX_LEN,w ; equal to received length?
bnz IntRestore ; no, receive more bytes
IntRxEnd:
bcf xfRcv ; yes, end of receiving data
movfw RX_XOR
skpnz ; xor-byte correct?
bsf xfNewMsg ; yes, received new packet
goto IntRestore
IntRxAck: ; Acknowledge transmision (send 0x20, 0x20)
bsf PORTB,0 ; MAX485 TX enable
movlwf 0x20,TXREG
BANK1 ;Bank 1
IntTxAck:
btfss TXSTA,TRMT ; wait until end of byte transmited
goto IntTxAck
BANK0 ;Bank 0
movlwf 0x20,TXREG ; send second byte
goto IntTxEnd ; wait until transmited
IntRxSend:
btfss xfNewTx ; new message to command station?
goto IntRestore ; no
bsf PORTB,0 ; MAX485 TX enable
BANK1 ;Bank 1
bsf PIE1,TXIE ; Enable TX interrupts
BANK0 ;Bank 0
clrf TXPOS
bsf xfXmit ; now transmiting
goto IntTxByte
IntError:
movfw RCREG ; 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 xfXmit ; yes, still transmiting?
goto IntTxEnd ; no, disable TX interrupts
IntTxByte:
movlw BUFTXINI ; calc buffer position
addwf TXPOS,w
movwf FSR
movfwf INDF,TXREG ; get byte from buffer and send it
incf TXPOS,f
decfsz TXCNT,f ; last byte?
goto IntRestore ; no
bcf xfNewTx ; yes, stop transmiting
bcf xfXmit
IntTxEnd:
BANK1 ;Bank 1
IntTxDone:
btfss TXSTA,TRMT ; wait until end of last byte transmited
goto IntTxDone
bcf PIE1,TXIE ; Disable TX interrupts
DNOP
DNOP
DNOP
BANK0 ;Bank 0
bcf PORTB,0 ; MAX485 TX disable
IntRestore:
movfwf SaveFSR,FSR ; Restore the Context Registers and Return
; movfwf SavePCLATH,PCLATH
swapf SaveSTATUS,w
movwf STATUS
swapf SaveW,f
swapf SaveW,w
retfie
; ----- Tables on first 256 bytes
Seg7Tab:
addwf PCL,f ; return 7segment patterns for 0..9
dt _0,_1,_2,_3,_4,_5,_6,_7,_8,_9;_A,_b,_c,_d,_E,_F
KeyServ:
btfsc ServiceMode ; discard in Service Mode
clrf keyNew
tstf keyNew
skpnz
return ; do nothing when no key pressed
; btfsc keyNew,0
clrw ; zero for SEL key
btfsc keyNew,5 ; F0/+ key
movlw 0x01 ; one for F0/+ key
btfsc keyNew,6 ; F1/- key
movlw 0x02 ; two for F1/- key
btfsc keyNew,1 ; STOP key
movlw 0x03 ; three for STOP key
clrf keyNew ; eat up
addwf MENU,w ; apply to current active menu
addwf PCL,f ; jump to options
;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 exceed page 0!"
endif
; ----- Initialization
INIT:
clrf RCSTA ; disable UART
movlwf 0x19,PORTA ; display off, receive, prepare SEL key for query
clrf PORTB ; all zero
BANK1 ;Bank 1
movlwf 0x06,TRISB ; all outputs except serial port
movlwf 0x26,TRISA ; RA1,RA2: comparator input,RA5: key input
movlwf 0x20,PIE1 ; RX interrupt (peripheral interrupt)
movlwf 0x81,OPTION_REG ; 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
movlwf 0x0B,PCON ; internal oscillator to 4MHz default
movlwf 0x00,VRCON ; VRR off
BANK0 ;Bank 0
movlwf 0x05,CMCON ; RA1, RA2 analog inputs, One comparator No.2
movlwf 0x31,T1CON ; Timer 1. Run, set 1:8 Prescaler, overflow: 0.5 s
;movlwf 0x04,T2CON ; Timer 2. Run with 1:1 Prescaler
movlwf 0x20,FSR ; Clear variables bank0
_ClearRAM
clrf INDF
incf FSR,f
btfss FSR,7
goto _ClearRAM
bsf segMask,0 ; always have one bit set there
call SetMyAddress ; get Xbus address
call UART_INI ; Init peripheals
;bcf PORTB,0 ; MAX485 TX disable just to be sure
call IniPWM ;
movlwf 0xC0,INTCON ; GIE enable, PEIE enable
movlwf _MI,digitH ; Diplay '--'
movwf digitL
movlwf 0xFF,FLASH ; no flash
btfsc PORTA,5 ; Key input (SEL pressed while startup?)
goto Setup
movlw LOK_MEM ; get last loco used
call EE_Read
movwf CURRADR
clrf CURRSPD ; stop default
movlwf b'01100000',CURRFNC ; default 28 steps, forward, FL,F1..F4 off
call GetVersion ; get command station version & type
clrf TMR1L ; time to transmit to command station
clrf TMR1H
bcf PIR1,TMR1IF
FindStation:
call idlepoll
btfsc PIR1,TMR1IF
call NotFound ; timeout
btfsc xfNewTx ; packet sent, command station present
goto FindStation
call GetStatus ; get current status
movlwf 0x01,T1CON ; Timer 1. Run, set 1:1 Prescaler, overflow: 66 ms
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
movlwf 0x10,MSB
MainStabilize:
call idlepoll
btfss PIR1,TMR1IF ; wait stabilize time 1 sec
goto MainStabilize
bcf PIR1,TMR1IF
decfsz MSB,f
goto MainStabilize
Loop:
call idlepoll
btfsc PIR1,TMR1IF ; read pot value if needed
call FlashPotSpeed
call KeyServ ; On new key do action
goto Loop
potPoll:
btfss PIR1,TMR2IF ; Bit 1
return ; no overflow of PWM timing generator (after postscaling)
bcf PIR1,TMR2IF
if 0
;check status of analog comparator and increment or decrement the PWM width
btfss CMCON,C2OUT ;1 if potentiometer voltage is higher than PWM DAC voltage
goto _ppLower
movlw 99
xorwf potPWM,w
skpz ; not above 99
incf potPWM,f
goto _ppSet
_ppLower:
tstf potPWM
skpz ; not below zero
decf potPWM,f
;Set CCPR1L and CCP1CON to the new 10-bit PWM value
;Non-atomic setting process allows slight jitter if a Timer2 overflow occurs meanwhile
_ppSet: movlwf 0x0C,CCP1CON ; clear CCP1X and CCP1X
swapf potPWM,w
andlw 0x30 ; take the two LSB to right position
iorwf CCP1CON,f ; set CCP1X and CCP1Y accordingly
rrf potPWM,w
movwf FSR ; use as temporary register (no WREG available)
rrf FSR,w
andlw 0x1F ; maximum 99>>4 == 24
movwf CCPR1L ; (this value is taken for the next PWM cycle)
;Accumulate current potPWM 256 times to get a mean value
movfw potPWM
addwf potFiltL,f
skpnc
incf potFiltH,f
incfsz potFiltN,f
return ; 256 summations not reached
;Set the newly found mean value for further processing
movfwf potFiltH,potValue
clrf potFiltL ; prepare accumulator for next accumulation
clrf potFiltH
bsf flPotValue ; flag having a new value (each 102.4 ms)
endif
return
;idle handler
idlepoll:
call potPoll ; get "ADC" value
call Poll1ms ; update display and keyboard
btfsc xfNewMsg ; new message received?
call HandleMsg ; yes, handle it
return
;end idle handler
; --- No Command Station avaiable
NotFound:
bcf PIR1,TMR1IF
movlwf _E,digitH ; show error 'E7'. SimpleMaus not addressed
movlwf _7,digitL
return
; --- Setup Loop
Setup:
call K_EnterSetup ; Select Menu setup
movlwf 0x01,T1CON ; Timer1 Run, set 1:1 Prescaler, overflow: 66 ms
SetupLoop:
;btfsc PIR1,TMR1IF ; show pot value if needed
btfsc flPotValue
call ShowPot
call potPoll ; get "ADC" value
call Poll1ms ; update display and keyboard
call KeyServ ; On new key do action
goto SetupLoop
; --- Xbus Incoming messages
HandleMsg: ; Handle incoming messages
Handle6X:
movfw HEADER
Handle61:
xorlw 0x61
bnz Handle6X_2
Handle61_0:
movfw DATA1
bnz Handle61_1
btfsc EmergOff ; second transmision
goto HandleEnd
bcf ServiceMode ; Track power off
bsf EmergOff
bcf EmergStop
HandleOff:
movlwf _E,digitH ; display 'E0'
movlwf _0,digitL
call SetFlash
goto HandleHomeEnd
Handle61_1:
xorlw 0x01
bnz Handle61_2
bcf ServiceMode ; normal operations resume
bcf EmergOff
bcf EmergStop
HandleResume:
movfw CURRADR ; show current loco
call DispBin
call SetFlash
movfw DCC_STATUS ; second transmision?
andlw b'00001011'
bz HandleHomeEnd ; yes, prevent hanging
bcf xfNewMsg ; message processed
call GetLocoInfo ; get loco info
goto HandleHomeEnd
Handle61_2:
xorlw (0x01 ^ 0x02)
bnz HandleEnd
btfsc ServiceMode ; second transmision
goto HandleEnd
bsf ServiceMode ; Service Mode entry
bcf EmergOff
bcf EmergStop
HandleServ:
movlwf _5,digitH ; display 'SP'
movlwf _P,digitL
call SetFlash
goto HandleHomeEnd
Handle6X_2:
xorlw (0x61 ^ 0x62)
bnz Handle6X_3
Handle62:
movfw DATA1
xorlw 0x22
bnz HandleEnd
movfwf DATA2,DCC_STATUS; Commnad station status response
btfsc ServiceMode ; set correct status
goto HandleServ
btfsc EmergOff
goto HandleOff
btfsc EmergStop
goto HandleStop
goto HandleResume
Handle6X_3:
xorlw (0x62 ^ 0x63)
bnz Handle8X
Handle63:
movfw DATA1
xorlw 0x21
bnz HandleEnd
movfwf DATA2,XBUS_VER ; software version
movfwf DATA3,XBUS_STA
goto HandleEnd
Handle8X:
movfw HEADER
Handle81:
xorlw 0x81
bnz HandleEX
movfw DATA1
bnz HandleEnd
btfsc EmergStop ; second transmision
goto HandleEnd
bcf ServiceMode ; Emergency stop
bcf EmergOff
bsf EmergStop
HandleStop:
movlwf _E,digitH ; show error 'ES'. Emergency stop
movlwf _5,digitL
call SetFlash
goto HandleHomeEnd
HandleEX:
movfw HEADER
HandleEX_1:
xorlw 0xE1 ; MU+DH errors
bnz HandleEX_2
HandleE1:
movfw DATA1
xorlw 0x88 ; stack full
bnz HandleEnd
HandleFull:
movlwf _5,digitH ; show error 'S-'. Stack full
movlwf _MI,digitL
movlwf b'11001100',FLASH ; fast flashing
goto HandleEnd
HandleEX_2:
xorlw (0xE1 ^ 0xE2)
bnz HandleEX_3
HandleE2:
movfw DATA1 ; Loco information
andlw 0xF0
xorlw 0x20
bnz HandleEnd
bsf flMultiUnit ; set Multi Unit
movfwf CURRADR,CURRMTR ; no functions
clrf CURRFNC
goto HandleE4_Data
HandleEX_3:
xorlw (0xE2 ^ 0xE3) ; Loco is being operated by another device
bnz HandleEX_4
movfw DATA1 ; Loco information
xorlw 0x40
bnz HandleEnd
movfw DATA2 ; check with CURRADR
andlw 0x3F
bnz HandleEnd
movfw DATA3
xorwf CURRADR,w
bnz HandleEnd
bsf flLocoOther ; operated by other
call SetFlash
bsf Test_AD
goto HandleEnd
HandleEX_4:
xorlw (0xE3 ^ 0xE4)
bnz HandleEX_5
HandleE4_0:
movfw DATA1 ; Loco information normal locomotive
andlw 0xF0
bnz HandleEnd
bcf flMultiUnit
movfwf CURRADR,CURRMTR ; normal locomotive
HandleE4_Loco:
movfw DATA3 ; 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
movfw DATA2
andlw 0x7F
movwf CURRSPD
bcf flLocoOther ; loco controlled by other device?
btfsc DATA1,3
bsf flLocoOther
call SetFlash
bsf Test_AD
goto HandleEnd
HandleEX_5:
xorlw 0xE4^0xE5
bnz HandleEnd
HandleE5_1:
movfw DATA1 ; Loco information in multi-unit
andlw 0xF0
xorlw 0x10
bnz HandleEnd
bsf flMultiUnit
movfwf DATA5,CURRMTR ; Loco in a Multi Unit
goto HandleE4_Loco
HandleHomeEnd:
movlwf MENU_RUN,MENU
call SetFlash
HandleEnd:
bcf xfNewMsg ; 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
movfw CURRADR ; 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
movfw CURRADR ; AL
call SendData
movfw CURRSPD
btfsc CURRFNC,LocoDir
iorlw 0x80
call SendData
SaveCurrentLoco:
bcf flLocoOther
call SetFlash
movfwf CURRADR,EEDATA0 ; save current used loco
movlw LOK_MEM
call SaveParam
goto SendMsg
FuncOperations:
movlw 0xE4 ; Function operations request
call SendHeader
movlw 0x20
call SendData
movlw 0x00 ; AH
call SendData
movfw CURRADR ; AL
call SendData
movfw CURRFNC ; Function group 1
andlw 0x1F
call SendData
goto SaveCurrentLoco
; --- XpressNet messages routines
SendHeader:
movwf DUMMY
SendHeaderW:
btfsc xfNewMsg ; new message received?
call HandleMsg ; yes, handle it
btfsc xfNewTx ; last message sent?
goto SendHeaderW ; no, wait
clrf TXPOS ; =0
clrf TXCNT ; byte count
movfw DUMMY
SendData:
movwf TEMP
movlw BUFTXINI
addwf TXCNT,w
movwf FSR
movfwf TEMP,INDF
xorwf TXPOS,f
incf TXCNT,f
return
SendMsg:
movfw TXPOS ; Xor byte
call SendData
bsf xfNewTx ; 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)
skpnc
bsf MY_ADR,7
return
Poll1ms:
btfss INTCON,T0IF
return
; --- Display+Keyboard+Potentiometer scanning (every 1 ms)
bcf INTCON,T0IF
;Collect key states for every anode line (up to 7 keys)
;As 1 ms after last display activity has been gone, there is much settling time at this point
movfw segMask
btfsc PORTA,5 ; Key input driven high?
iorwf keyCollect,f ; if yes, remember current segMask bit
;Read potentiometer
;call ReadPot
;Display: Switch to next segment and show up to 2 in parallel
movlwf 0x18,PORTA ; both cathodes high == display off
clrc
rlf segMask,f ; Segment bit 0..6
btfsc segMask,7 ;(ignore set bit 7)
bsf segMask,0
call mask2Display ; put the 7 bits to the 7 outputs
;(can be easily changed to 8 bit for decimal point support if there were another pin free)
btfss FLASH,7 ; flashing?
return ; dark phase, leave cathodes off
movfw digitL
andwf segMask,w ; Is this segment on for right digit?
skpz
bcf PORTA,4 ; right digit: enable cathode
movfw digitH
andwf segMask,w ; Is this segment on for left digit?
skpz
bcf PORTA,3 ; left digit: enable cathode
btfss segMask,0 ; handle key events every 7 ms
return
;Keys: Handle key changes every full display cycle (7 ms) - IMHO no bouncing check needed!
comf keyState,w
andwf keyCollect,w
movwf keyNew ; keys that changed from released to pressed
bz _nonew
movlwf 500/7,keyRepeat ; next event after 0.5 s
_nonew:
movfwf keyCollect,keyState ; keys currently pressed
clrf keyCollect
movfw keyState ; Any keys still pressed?
skpnz
return ; no, do nothing, or let keyNew as-is
decfsz keyRepeat,f ; count-down
return
movwf keyNew ; copy keyState to keyNew
movlwf 200/7,keyRepeat ; next key event after 0.2 s (faster)
return
mask2Display: ;outputs the current digit mask to the 7 anode outputs, one at a time.
;(For charlieplexing, use TRIS instead of PORT registers later.)
;PROBLEM: As output latch cannot be read back, inputs held low in this moment loose their pullup, and vice versa.
;(Moreover, outputs driving high and shortened to ground will then drive low, and vice versa.)
;Disabling interrupts will not help. Luckily, only PortB has pullups and these are disabled.
movlw b'00111110' ; display off fgXXXXXa
andwf PORTA,f
movlw b'00001111' ; edcbXXXX
andwf PORTB,f
btfsc segMask,0 ;a
bsf PORTA,0
btfsc segMask,5 ;f
bsf PORTA,7
btfsc segMask,6 ;g
bsf PORTA,6
rlf segMask,w ;gfedcbaX
movwf FSR ;use as temporary register
rlf FSR,f ;fedcbaXg
rlf FSR,w ;edcbaXgf
andlw 0xF0 ;edcb0000
iorwf PORTB,f ;allright
return
; ---- Display
DispBin: ;out binary number <w> = 00..99 or "OL"
;sublw 99
;bc _l2
;sublw 99
;Simply repeat subtracting 10 until underflow occurs
;No check is done whether <w> is > 99!!
movwf digitL ; As display multiplexing isn't done in an ISR,
clrf digitH ; digit positions can be easily reused for this purpose.
movlw 10
_l1: incf digitH,f
subwf digitL,f
bc _l1 ; loop while no underflow at subtraction; inverted carry
addwf digitL,w ; back-correction and load
call Seg7Tab ; now convert 0..9 to 7segment codes
movwf digitL
decf digitH,w ; back-correction and load
call Seg7Tab
_l3: movwf digitH
return
;_l2: movlwf _L,digitL ; show overflow (it's a runtime error)
; movlw _0
; goto _l3
SetFlash:
movlw b'11111111' ; always on
movf MENU,f
skpz
movlw b'11011111' ; minimum flashing
btfsc flLocoOther
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
; ----- Menu Keys
K_EnterLok:
movlwf _L,digitH ; Menu Loco selection. Diplay 'L '
clrf digitL
movlwf MENU_LOK,MENU
call SetFlash
return
K_LokUp:
incf CURRADR,f ; Increment Loco number
movlw 100
xorwf CURRADR,w
movlw 1
skpnz
movwf CURRADR
K_LokEnd:
call GetLocoInfo
movfw CURRADR
goto DispBin
K_LokDwn:
decf CURRADR,f ; Decrement Loco number
movlw 99
skpnz
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 _DN ; '_' F2 status ***
btfsc CURRFNC,1
movlw _UP ; '~'
movwf digitL
movlw _DN ; '_' F1 status
btfsc CURRFNC,0
movlw _UP ; '~'
movwf digitH
movlw MENU_FNC
movwf MENU
call SetFlash
return
K_ChangeDir:
; tstf SPEED ; Change Direction only in speed 0 ***
; skpz
; return
movlw 0x20 ; Change current loco direction
xorwf CURRFNC,f
call LocoOperations
K_EnterDir:
movfw SPEED ; Change Dir / steps only in speed 0
bnz K_EnterRun
btfss CURRFNC,LocoDir ; Menu Direction/Steps.
goto K_EnterDirBack
; Forward 'd-'
movlwf _d,digitH ; 'd'
movlw _DN ; '_' 14 steps
btfsc CURRFNC,Loco28
movlw _MI ; '-' 28 steps
btfsc CURRFNC,Loco128
movlw _UP ; '~' 128 steps
movwf digitL
goto K_EnterDirEnd
K_EnterDirBack: ; Backward '-d'
movlw _DN ; '_' 14 steps
btfsc CURRFNC,Loco28
movlw _MI ; '-' 28 steps
btfsc CURRFNC,Loco128
movlw _UP ; '~' 128 steps
movwf digitH
movlw _d ; 'd'
movwf digitL
K_EnterDirEnd:
movlwf MENU_DIR,MENU
call SetFlash
return
K_ChangeDirRun:
movlw 0x20 ; Change current loco direction ***
xorwf CURRFNC,f
call LocoOperations
K_EnterRun:
movlwf MENU_RUN,MENU ; Main menu, show loco number
call SetFlash
movfw CURRADR
goto DispBin
K_ChangeStep:
movfw SPEED ; only if speed 0
skpz
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
skpnz
movwf EEDATA0
K_XbusUpdate:
movlw ADR_MEM
call SaveParam
movfw EEDATA0
goto DispBin
K_XbusDwn:
movlw ADR_MEM ; Decrement Xbus address
call EE_Read
movwf EEDATA0
decf EEDATA0,f
movlw 31
skpnz
movwf EEDATA0
goto K_XbusUpdate
K_SelSetup:
btfss flSelSetup ; Change setup menu selection
goto K_EnterSetupPot
K_EnterSetup:
bcf flSelSetup
movlw _A ; show 'A '
goto _ese
K_EnterSetupPot:
bsf flSelSetup
movlw _P ; show 'P '
_ese:
movwf digitH
clrf digitL
movlwf MENU_SETUP,MENU
return
K_EnterPar:
movlw MENU_POT ; Set selected setup menu
btfss flSelSetup
movlw MENU_XBUS
movwf MENU
btfsc flSelSetup
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:
movfwf EEDATA0,OFFSET ; set new offset
movlw POT_MEM
goto SaveParam
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:
BANK1
;movlwf 51,PR2 ; Set PWM freq = 19200 Hz (R = 2.7 kΩ, C = 100 nF, τ = 270 µs, f ≈ 600 Hz)
movlwf 99>>4,PR2 ; PWM frequency = 40 kHz allows DAC values between 0 and 99 (= handy limits)
BANK0
movlwf 0x7C,T2CON ; enable TMR2 1:1 prescaler, 1:16 postscaler (400 µs between interrupts, 2.5 kHz)
movlw POT_MEM ; read pot. offset
call EE_Read
andlw 0x0F
movwf OFFSET
; movlwf 0x1F,POT_CNT ; init pot counter
; addwf OFFSET,w
; movwf CCPR1L ; analog output max. voltage (5bits + 2bits)
movlwf 0x0C,CCP1CON ; enable PWM mode
return
; copy POT_CNT to next POT_SAVED array (every 1 ms)
if 0
ReadPot:
movfw POT_INDEX
andlw 0x07
addlw POT_SAVED
movwf FSR
btfss CMCON,C2OUT ;1 if potentiometer voltage is higher than PWM DAC voltage
goto ReadPotNxt ;0: Lower PWM DAC voltage
movfwf POT_CNT,INDF ; yes, get position, 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:
movlwf 0x1F,POT_CNT ; reload max. voltage
addwf OFFSET,w ; pot. offset
movwf CCPR1L
return
endif
;Read potentiometer value from RAM and — if changed — send it to loco
FlashPotSpeed:
bcf PIR1,TMR1IF ; calc flashing sequence
clrc
btfsc FLASH,7
setc
rlf FLASH,f
;PotSpeed:
btfsc xfNewTx ; don't read if waiting to send
return
if 0
movfw POT_SAVED ; calc mean 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
endif
rrf potValue,w ;0..49
andlw 0x1F ;0..31
movwf TEMP
xorwf SPEED,w ; pot equal to last position?
skpnz
return ; yes, nothing to do
movfwf TEMP,SPEED ; save new position
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
skpz
xorlw 0x01
movwf CURRSPD ; update speed
goto LocoOperations
PotSpd28:
movlw 0x04 ; skip emergency & stop
subwf TEMP,w
skpc
clrf TEMP
rrf TEMP,w ; set bit 4 as LSB
skpnc
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
bcf flPotValue
movfw MENU ; show only in Setup Pot. Menu
xorlw MENU_POT
skpz
return
#if 0
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
#endif
movfw potValue
goto DispBin
; ----- Serial port routines (XBus)
UART_INI:
bcf PORTB,0 ; Disable TX
clrf XFLAGS ; clear XpressNet flags
clrf RX_LEN ; clear input buffer
BANK1 ;Bank 1
movlwf ((10*FXTAL/(16*BaudRate))+5)/10-1,SPBRG ; BRGH = 1
movlwf 0x64,TXSTA ; 9 bit, Enable TX, BRGH=1, TXD9=0
BANK0 ;Bank 0
movlwf 0xD0,RCSTA ; Enable RX, 9 bit
clrw ; Settling time
SETTLE:
addlw 0xFF
bnz SETTLE
movfw RCREG ; flush receive buffer
movfw RCREG
movfw RCREG
return
;----- Internal EEPROM routines ------------------------------------------------
EE_Read: ;w=address, returns w=data
BANK1
movwf EEADR
bsf EECON1,RD
movfw EEDATA
BANK0
return
SaveParam: ; w=address, EEDATA0=data. Write only changes
call EE_Read
xorwf EEDATA0,w
skpnz
return
movfw EEDATA0
BANK1
movwf EEDATA
bsf EECON1,WREN
bcf INTCON,GIE
movlwf 0x55,EECON2
movlwf 0xAA,EECON2
bsf EECON1,WR
bsf INTCON,GIE
bcf EECON1,WREN
EEWrite0:
btfsc EECON1,WR
goto EEWrite0
BANK0
return
; ----- EEPROM default values
org 0x2100
dw 0x01 ; XpressNet device address
dw 0x03 ; Last Locomotive used
dw 0x00 ; Potentiometer offset
org 0x2110 ;EEPROM
dt "Simple Maus v.3\n"
dt "F.Cañada->H.Haftmann\n"
dt "19/01/30"
end
Vorgefundene Kodierung: UTF-8 | 0
|