;************************************************************************
; TaPRK IR-LINK-RECEIVER
;************************************************************************

	LIST P = 16F84A,  R = HEX		;Prosessortype, radix
	INCLUDE	"p16f84A.inc"			;w,f,porta,portb etc. 
	ERRORLEVEL -224				;prevents error from
						;"tris"-command
        __CONFIG _PWRTE_ON & _HS_OSC & _WDT_OFF   

;************************************************************************
; SCHEMATIC DIAGRAM
;************************************************************************

;	          		  +---+_+---+
;		N/C	PORTA.2 ->|1      18|<- PORTA.1 N/C
;		N/C	PORTA.3 ->|2      17|<- PORTA.0	TSOP1738 act.lo
;			PORTA.4 ->|3      16|-- XTAL
;			    Vdd --|4      15|-- XTAL
;			Vss(GND)--|5      14|-- Vdd (+5V)
;	Strobe_MUX	PORTB.0 <-|6      13|-> PORTB.7	LED1
;	Strobe_Disp	PORTB.1 <-|7      12|-> PORTB.6	N/C
;	Comm.Ser.data	PORTB.2 <-|8      11|-> PORTB.5	N/C
;	Comm.Ser.clock	PORTB.3 <-|9      10|-> PORTB.4	N/C
;		    	          +---------+

;************************************************************************
; MAIN IDEA
;************************************************************************
;
;	This is based on Holger Klabunde's design, but is completely
;	rewritten in assembler.
;
;	Timing
;	======
;	26us cycle of IR-LED on/off gives 38,5kHz
;	16 cycles for 1/2 bit -> 16 x 26us= 416 us
;	Prescaler of 1:8 makes TMR0 advance every 8us (@4MHz crystal)
;	1/2 bit needs 416/8 = 52 ticks of TMR0
;	1/4 bit needs  52/2 = 26 ticks of TMR0
;
;	This is loosely following Philips RC5, exept:
;
;	-- Bit length 2 X 416us!
;	-- Only one start bit, and it is "0"
;	-- Only 8 data bits
;	-- Added parity bit
;	-- Total of 10 bits
;
;	This is presumably the same as Lorell Joiner's
;	setup, done with hardwired UART some 10 years ago, but instead
;	of using 250 kHz carrier and on/off bits, I use the 
;	Bit formation same as RC5 at 36...38 kHz carrier:
;
;	+---+                 +---+
;	|   |     =0          |   |    =1
;       +   +---+         +---+   +
;
;	TSOP1738 receiver module inverts the above
;
;	Serial output to 7-segment display and C-MOS switches
;	4094 serial-in-parallel-out will be used. Two branches, one
;	for controllers (PC-Joystick through C-MOS switches) and the
;	other for speed step (hex-) display. Both branches may use
;	common data and clock, strobe sent only to correct branch.
;
;	I'll use 4066 to connect a set of trimmers to replace
;	joystick pot.
;
;	Functions (F0 and F1) will be added later!
;
;	==============================================================
;	5-PIN STEREO DIN DISP. UNIT 
;	--------------------------------------------------------------
;		1 CK	 Orange  [Biltema
;		4 GND	 Green	  keyboard
;		2 DATA	 Blue	  extension
;		5 +5V	 White	  cord
;		3 STROBE Black	  colours]

;	==============================================================
;	3.5mm STEREO IR-MODULE CONNECTOR 
;	--------------------------------------------------------------
;		Tip	Data
;		Ring	+
;		Sleeve	GND

;	==============================================================
;	RESISTOR NETWORK VALUES (eq.47K pot)
;	--------------------------------------------------------------
;		1	2K9
;		2	6K
;		4	11K9
;		8	23K5
;	--------------------------------------------------------------
;	4094 switch on-resistance 250R/switch, so all cable throttles
;	will be equipped with a 1K resistor in addition to pot to
;	compensate as this will not go to 0 ohms. Will also change
;	cable throttles to have max. speed at min resistance in case
;	of power failure in this setup!
;	
;************************************************************************
; VARIABLES
;************************************************************************

	CBLOCK H'0C'

		Data1		;Data from IR-receiver
		Data2		;waiting for conversion to 7-seg...
		Parity		;Parity
		Err		;error flag from ReadBit
		bitcount	;bit counter

		count_gp	;freewheeling counter

		speed_0		;cab 0 speed (4 bits)
		speed_1		;cab 1 speed (4 bits)
		speed_2		;cab 2 speed (4 bits)
		speed_3		;cab 3 speed (4 bits)

	ENDC

;************************************************************************
; STATICS
;************************************************************************

	#DEFINE TSOP		PORTA, 0 	;IR receiver
	#DEFINE M_STROBE	PORTB, 1 	;MUX Strobe
	#DEFINE S_STROBE	PORTB, 0 	;7-SEG Strobe
	#DEFINE S_DATA		PORTB, 2 	;7-SEG Data
	#DEFINE S_CLOCK		PORTB, 3 	;7-SEG Clock
	#DEFINE	LED1		PORTB, 7	;Pilot light
	#DEFINE HALFBIT		.203 		;= 255-52
	#DEFINE QUADBIT		.229		;= 255-26

;************************************************************************
;RESTART/INT-START
;************************************************************************

	ORG     0		; Restart vector
	goto	main

	ORG	4		; INT vector
	retfie

;------------------------------------------------------------------------
; Initialize I/O ports, Interrupts, Timers etc
;------------------------------------------------------------------------
init

	movlw	b'00000000'	;RB0..7=out
	tris	portb		;
	movlw	b'00011111'	;RA0..4=in
	tris	porta		;
				;easy to recall:
				;Input -> I -> 1
				;Output-> O -> 0

	clrf	porta		;clear Port A (Shift reg output)
	clrf	portb		;Port B on (LED test)

	CLRWDT			;
	bsf	STATUS, RP0	;
	movlw	b'00000010'	;Pullup=on, Prescaler= 1:8
	movwf	OPTION_REG	;
	bcf	STATUS, RP0	;

ramc	movlw	0x0c		; start of 16F84 ram
	movwf	fsr
ramc1	clrf	indf		; clear indirect location
	incf	fsr, f		; next location
	btfss	fsr, 7		; cleans up to 0x7f
	goto	ramc1		; [From J.Niinikoski]

;	clrf	speed_0		;set speed_x to zero
;	clrf	speed_1		;set speed_x to zero
;	clrf	speed_2		;set speed_x to zero
;	clrf	speed_3		;set speed_x to zero
;	clrf	DATA1		;set speed_x to zero
;	clrf	DATA2		;set speed_x to zero

	movfw	DATA1		;clear w

	call disp_speed		;Show speed w. 7-seg display
	call mux_speed		;Drive mux to operate speed network

	return


;------------------------------------------------------------------------
; Increment cab's typematic counters
;------------------------------------------------------------------------

IncCounters
	tstf	count_gp	;set zero flag from counter
	skpz			;skip if zero
	incf	count_gp, f	;not yet -> increment
	return			;[21/4-2002]

;------------------------------------------------------------------------
; Read whole bit, set/clear error reset TMR0, count parity
;------------------------------------------------------------------------

ReadBit
	movlw	0xFF		;Set error state,
	movwf	ERR		;will be cleared if bit correctly read

rb1	btfss	INTCON, T0IF	;wait for timer to overflow
	goto 	rb1		;not yet

	movlw	HALFBIT		;new timer base
	addwf	TMR0, f		;add to just overflowed timer
	bcf	INTCON, T0IF	;clear overflow flag

	btfsc	TSOP		;test 1st halfbit state   	###
	goto 	read1		;was high -> beginning of "1"

read0	btfss	INTCON, T0IF	;wait for timer to overflow
	goto 	read0		;not yet

	movlw	HALFBIT		;new timer base
	addwf	TMR0, f		;add to just overflowed timer
	bcf	INTCON, T0IF	;clear overflow flag

	btfss	TSOP		;test 2nd halfbit, must be high (set) ###
	return			;was not, error, return

	bcf	STATUS, C	;have read a "0"
	rlf	DATA1, f	;move it into DATA1
	clrf	ERR		;clear error state
	return

read1	btfss	INTCON, T0IF	;wait for timer to overflow
	goto 	read1		;not yet

	movlw	HALFBIT		;new timer base
	addwf	TMR0, f		;add to just overflowed timer
	bcf	INTCON, T0IF	;clear overflow flag

	btfsc	TSOP		;test 2nd halfbit, must be low (clear) ###
	return			;was not, error, return

	bsf	STATUS, C	;have read a "1"
	rlf	DATA1, f	;move it into DATA1
	incf	Parity, f	;add 1 to parity
	clrf	ERR		;clear error state
	return

;---------------------------------------------------------------
;Lookup-table for 7-seg display, uses 4094 shift registers
;---------------------------------------------------------------

Lookup
	addwf	pcl,f		;
	retlw	b'00011000'	; 0
	retlw	b'01111011'	; 1
	retlw	b'00101100'	; 2
	retlw	b'00101001'	; 3	 77777
	retlw	b'01001011'	; 4	6     8
	retlw	b'10001001'	; 5	6     8
	retlw	b'10001000'	; 6	6     8
	retlw	b'00111011'	; 7      55555
	retlw	b'00001000'	; 8	1     3
	retlw	b'00001001'	; 9	1     3
	retlw	b'00001010'	; A	1     3  44
	retlw	b'11001000'	; b	 22222   44
	retlw	b'10011100'	; C
	retlw	b'01101000'	; d
	retlw	b'10001100'	; E
	retlw	b'10001110'	; F


;------------------------------------------------------------
; MUX out SPEED
;------------------------------------------------------------
mux_speed
	movf	speed_1, w	;speed_x to w
	btfsc	speed_1, 4	;Check for direction button
	iorlw	b'00010000'	;operate c-mos switch if set!
	call 	so_byte		;and shift out byte

	movf	speed_0, w	;speed_x to w
	btfsc	speed_0, 4	;Check for direction button
	iorlw	b'00010000'	;operate c-mos switch if set!
	call 	so_byte		;and shift out byte

	bsf	M_STROBE	;strobe out
	nop			;wait for slow C-MOS
	bcf	M_STROBE	;

	return

;------------------------------------------------------------
; DISPALY SPEED
;------------------------------------------------------------
Disp_speed
	movf	speed_1, w	;speed_x to w
	andlw	0xF		;clear top nibble
	call	lookup		;convert to 7-segment
	btfsc	speed_1, 4	;Check for direction button
	andlw	b'11110111'	;light (clear) 'dp' if set!

	call 	so_byte		;and shift out byte

	movf	speed_0, w	;speed_x to w
	andlw	0xF		;clear top nibble
	call	lookup		;convert to 7-segment

	btfsc	speed_0, 4	;Check for direction button
	andlw	b'11110111'	;light (clear) 'dp' if set!

	call 	so_byte		;and shift out byte

	bsf	S_STROBE	;strobe out
	nop			;wait for slow C-MOS
	bcf	S_STROBE	;

	return


;-----------------------------------------------------------------------
;shift out one byte from w (uses DATA1 and BITCOUNT)
;-----------------------------------------------------------------------

so_byte
	movwf	DATA1		;and put out for shifting

	movlw	.8		;all 8 bits
	movwf	BITCOUNT	;

sob1	rlf	DATA1, F	;rotate through carry (MSB first) and 
	btfsc	STATUS, C	;in case of carry shift out 1 
	goto	so1		;carry=1
so0				;carry=0

	bcf	S_DATA		;data  lo
	bsf	S_CLOCK		;clock hi
	nop			;wait for slow C-MOS
	bcf	S_CLOCK		;clock lo

	goto	sob2
so1

	bsf	S_DATA		;data  hi
	bsf	S_CLOCK		;clock hi
	nop			;wait for slow C-MOS
	bcf	S_CLOCK		;clock lo

sob2	decfsz	BITCOUNT, F	;all bits sent?
	goto 	sob1		;not yet, shift out more
	return			;yes, this byte shifted out
	

	
;------------------------------------------------------------------------
; MAIN PROGRAMME
;------------------------------------------------------------------------
main
	call 	init		;intialize ports
m1
	bsf	LED1		;pilot off
	btfss	TSOP		;wait for beginning of the broadcast ##
	goto 	m2		;found, start interpreting

	incfsz	count_gp, f	;set zero flag from counter_gp
	goto	m1		;not yet, wait for broadcast
	nop			;HERE F-section -> increment cab counters
	goto 	m1		;wait for broadcast
m2
	bcf	LED1		;pilot on
	movlw	QUADBIT		;advance 1/4 bit to hit middle of
	movwf	TMR0		;1st half-bit of start-bit ("0")
	bcf	INTCON, T0IF	;just in case: clear Timer overflow flag

	call	ReadBit		;read whole start-bit
	btfsc	ERR, 0		;if error, return to square one
	goto 	m1		;

	btfsc	DATA1, 0	;last read bit (LSB of DATA1) must
	goto 	m1		;be "0" as it was the start bit

	movlw	8		;read 8 bits
	movwf	BITCOUNT
	clrf	DATA1		;clear read DATA1
	clrf	Parity		;clear parity

m3	call 	ReadBit		;read whole DATA1 bit
	btfsc	ERR, 0		;if error, return to square one
	goto 	m1		;

	decfsz	BITCOUNT, f	;All DATA1 bits read?
	goto	m3		;not yet, read more

	movfw	DATA1		;all DATA1 read
	movwf	DATA2		;move to safety

	call	ReadBit		;read parity bit
	btfsc	ERR, 0		;if error, return to square one
	goto 	m1		;

	btfsc	Parity, 0	;Parity must now be clear
	goto	m1		;if not return to square one
	bcf	PORTA, 2

ad
;-----------------------------------------------------------------------
;ADDRESS TESTING

	btfss	DATA2, 7	;test for address MSB
	goto	adlo		;address=0/1
	goto	adhi		;address=2/3

adlo
	btfss	DATA2, 6	;test for address LSB
	goto	ad0		;address=0
	goto	ad1		;address=1

adhi
	btfss	DATA2, 6	;test for address LSB
	goto	ad2		;address=2
	goto	ad3		;address=3
;----------------------------------------------------------------
ad0

;	btfss	DATA2, 5	;test for Speed/Function bit
;	goto 	m4		;set (1) no F-stuff yet, skip to m4
	movfw	DATA2		;move DATA to w
	andlw	0x1f		;clear 3 top bits
	movwf	speed_0		;move to speed_0
	goto	m4

;----------------------------------------------------------------
ad1
;	btfss	DATA2, 5	;test for Speed/Function bit
;	goto 	m4		;set (1) no F-stuff yet, skip to m4
	movfw	DATA2		;move DATA to w
	andlw	0x1f		;clear 3 top bits
	movwf	speed_1		;move to speed_1
	goto	m4

;-------------------------------------------------------------------
ad2
;-------------------------------------------------------------------
ad3
	goto m4

m4
;	;----------------------------------------------------;
;	; LED DISPALY AT PORT B                              ;
;	; [This gets messed up if direction button pressed]  ;
;	;----------------------------------------------------;
;	swapf	speed_1, w	;speed of cab1 to high nibble
;	iorwf	speed_0, w	;set cab 0 bits hi also
;	movwf	DATA2		;move to ex. safety
;	comf	DATA2, w	;invert bits for output
;	movwf	PORTB		;move to display

m5

	call disp_speed		;Show speed w. 7-seg display
	call mux_speed		;Drive mux to operate speed network

	goto 	m1
	end

