Quelltext /~heha/basteln/m/Kram/SimpleMaus.zip/SimpleMaus_v3-x.a14

;======================================================================
;				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-80