;************************************************************************
; TaPRK IR-LINK-TRANSMITTER BUTTON VERSION
;************************************************************************

	LIST P = 16F84A,  R = HEX		;Prosessor type, radix
	INCLUDE	"p16f84A.inc"			;w,f,porta,portb etc. 
	ERRORLEVEL -224				;Prevent error message
						;from "tris"-command

        __CONFIG _PWRTE_OFF & _HS_OSC & _WDT_OFF;4MHz crystal   

;************************************************************************
; SCHEMATIC DIAGRAM
;************************************************************************

;	          		  +---+_+---+
;		N/C	PORTA.2 ->|1      18|<- PORTA.1 N/C
;		N/C	PORTA.3 ->|2      17|-> PORTA.0	IR-LED act.hi
;		N/C	PORTA.4 ->|3  PIC 16|-- XTAL (4MHz)
;		(+5V)	MCLR    --|4  16F 15|-- XTAL
;		(GND)	Vss     --|5  84A 14|-- Vdd (+5V)
;	ACCEL. SW	PORTB.0 ->|6      13|<- PORTB.7	ADD_hi
;	BRAKE  SW	PORTB.1 ->|7      12|<- PORTB.6	ADD_lo
;	DIR. BUTTON (R)	PORTB.2 ->|8      11|<- PORTB.5	CH-SWITCH
;	F0/5 BUTTON (Y)	PORTB.3 ->|9      10|<- PORTB.4	F1 BUTTON (G)
;		    	          +---------+

;************************************************************************
; MAIN IDEA
;************************************************************************
;	This is based on Holger Klabunde's design, but is completely
;	rewritten in assembler.
;
;	Sender timing, carrier, packet format
;	=====================================
;	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
;
;	[2002-03-07] Error in number of "nop"s, in off-section:
;	had 1 too many. Also noted that (acc. to datasheet)
;	adding to TMR0 will clear prescaler contents, so always 
;	a bit late in timing! Hint: Use "MPLAB/Window/Stopwatch"!
;
;	This is loosely following Philips RC5, exept:
;	Bit length now 832us instead of 1798us.
;	Only one start bit, and it is "0", only 8 data bits and
;	Added parity bit -> Total of 10 bits.
;
;	This is presumably similar to Lorell Joiner's
;	setup, done with hardwired UART some 10 years ago, (MR Jan 1990)
;	but instead of using 250 kHz carrier and on/off bits, I use the 
;	Bit formation similar to RC5 at 36...38 kHz carrier:
;
;	+++++                 +++++
;	|||||     =0          |||||    =1
;       +++++---+         +---+++++
;
;	Interval of packets needs to be somewhat random.
;	1 bit takes approx. 1 ms, one packet about 10 ms
;	1 sec => max. 100 packets
;	Sending now at 40ms + (10 * address) interval:
;	Address 0...3 -> 40...70 ms interval.
;
;	[5/5-02] Added more comments and F1+FL

;************************************************************************
; VARIABLES
;************************************************************************

	CBLOCK H'0C'

		Data1		;Data from IR-receiver
		Parity		;Parity
		bitcount	;bit counter
		nulcount	;repeat packet counter
		potvalue	;pot position
		oldpot		;previous pot value
		count_0		;typematic counter
	ENDC
;************************************************************************
; STATICS
;************************************************************************


	#DEFINE IRLED		PORTA, 0 	;IR LED

	#DEFINE ACCEL_SW	PORTB, 0 	;!Faster
	#DEFINE BRAKE_SW	PORTB, 1 	;!Slower
	#DEFINE DIR_BUTTON	PORTB, 2 	;!Direction/Emg.Stop
	#DEFINE FL_BUTTON	PORTB, 3 	;!FL/F5
	#DEFINE F1_BUTTON	PORTB, 4 	;!F1
	#DEFINE CH_SWITCH	PORTB, 5 	;!CH_SWITCH


	#DEFINE ADDRLO		PORTB, 6 	;!Address, low bit
	#DEFINE ADDRHI		PORTB, 7 	;!Address, high bit

	#DEFINE HALFBIT		.203 		;= 255-52
	#DEFINE QUADBIT		.229		;= 255-26

	constant MAXSPEED	=.15	 	;max reading for pot
	
;************************************************************************
;RESTART/INT-START
;************************************************************************

	ORG     0		; Restart vector
	goto	main

	ORG	4		; INT vector
	retfie

;------------------------------------------------------------------------
; Initialize I/O ports, Interrupts, Timers etc
;------------------------------------------------------------------------
init
	movlw	b'00000010'	;RA0=out, RA1=in RA2:4=out
	tris	porta
	movlw	b'11111111'	;RB0..7=in
	tris	portb

	CLRWDT			;
	bsf	STATUS, RP0	;
	movlw	b'00000010'	;Pullup=on, Prescaler= 1:8
	movwf	OPTION_REG	;
	bcf	STATUS, RP0	;
	clrf	potvalue	;speed to zero
	movlw	.10		;
	movwf	nulcount	;start by sending 10 packets

;------------------------------------------------------------------------
; Increment throttle's typematic counters
; 
;	If faster/slower buttons are pressed long enough
;	the speed will be incremented again. Count_0
;	is used for differentiating single click and continuous
;	press (needing typematic updating)
;------------------------------------------------------------------------
IncCounters
	tstf	count_0		;test zero flag from counter
	skpz			;skip if zero
	incf	count_0, f	;not yet -> increment
	return

;------------------------------------------------------------------------
; Send 0 [###___]
;	This will send zero bit. The zero bit consists of
;	half bit length of 38,5 kHz pulses, i.e. 13 instr.
;	cycles on, 13 instr. cycles off. 
;	The other half of byte length is "darkness"
;------------------------------------------------------------------------
send0
s01	bsf	IRLED		;IR-LED "on" for 13 instr. cycles

	nop
	nop
	nop
	nop
	nop
	
	nop
	nop
	nop
	nop
	nop

	nop
	nop

	bcf	IRLED		;IR-LED "off" for 13 instr. cycles

	nop
	nop
	nop
	nop
	nop
	
	nop
	nop
	nop
	nop

	btfss	INTCON,	T0IF	;1/2 bit time gone?
	goto	s01		;not yet, send another burst

	bcf	INTCON, T0IF	;clear overflow flag
	movlw	HALFBIT		;new timer base
	addwf	TMR0, f		;add to just overflowed timer

s02	btfss	INTCON,	T0IF	;1/2 bit time gone?
	goto	s02		;not yet

	bcf	INTCON, T0IF	;clear overflow flag
	movlw	HALFBIT		;new timer base
	addwf	TMR0, f		;add to just overflowed timer

	return

;------------------------------------------------------------------------
; Send 1 [___###]
;	This will send "one" bit. The zero bit consists of
;	half bit length of darkness and second half byte length of 
;	38,5 kHz pulses, i.e. 13 instr.	cycles on, 13 instr. cycles off. 
;------------------------------------------------------------------------
send1
s11	btfss	INTCON,	T0IF	;1/2 bit time gone?
	goto	s11		;not yet

	incf 	PARITY, F	;parity++

	bcf	INTCON, T0IF	;clear overflow flag
	movlw	HALFBIT		;new timer base
	addwf	TMR0, f		;add to just overflowed timer

s12	bsf	IRLED		;IR-LED "on" for 13 instr. cycles

	nop
	nop
	nop
	nop
	nop
	
	nop
	nop
	nop
	nop
	nop

	nop
	nop

	bcf	IRLED		;IR-LED "off" for 13 instr. cycles

	nop
	nop
	nop
	nop
	nop
	
	nop
	nop
	nop
	nop

	btfss	INTCON,	T0IF	;1/2 bit time gone?
	goto	s12		;not yet, send another burst

	bcf	INTCON, T0IF	;clear overflow flag
	movlw	HALFBIT		;new timer base
	addwf	TMR0, f		;add to just overflowed timer

	return

;------------------------------------------------------------------------
;Read switch
;	This will read the speed, direction and function button presses.
;	First the direction button is tested -- if speed is non-zero
;	it will act as emg. brake -> potvalue to zero
;	Next the acceleration and brake-buttons are tested
;	and in case typematic counter has advanced to non-zero value
;	nothing is done (IncCounter subroutine will increment count_0 
;	until it overflows to zero). If count_0 is zero, potvalue
;	is adjusted within limits and count_0 is given a non-zero seed.
;	If no button is pressed, then count_0 is cleared to zero.
;------------------------------------------------------------------------
read_sw
	tstf	potvalue	;check if speed
	skpnz			;is zero?
	goto	rsw1		;yes, no need for emg.stop
	btfss	DIR_BUTTON	;no, check if button is pressed
	clrf	potvalue	;yes, speed -> 0
rsw1
	btfsc	ACCEL_SW	;faster-bit cleared?
	goto	rsw1s		;no let's go to test slower-button
rsw1f
	tstf	count_0		;check if typematic still on
	skpz			;skip if typematic clear
	return			;no, pass
	movlw	.240		;Typematic seed is now 240
	movwf	count_0		;start typematic counter
	incf	potvalue, f	;accelerate speed 
	btfss	potvalue, 4	;going too fast?
	return			;no, done with this
	movlw	0x0f		;top speed is 15
	movwf	potvalue	;and we settle for that!
	return			;done!

rsw1s	btfsc	BRAKE_SW	;slower-bit cleared?
	goto	rsw1nop		;no, no action here, goto cab_1 nop
	tstf	count_0		;check if typematic still on
	skpz			;skip if typematic clear
	return			;no, pass
	movlw	.240		;Typematic seed is now 200
	movwf	count_0		;start typematic counter
	tstf	potvalue	;test speed etting
	skpz			;already standstill?
	decf	potvalue, f	;no, let's put on brakes
	return			;done!

rsw1nop	clrf	count_0		;clear typematic counter
	return			;and done!

;------------------------------------------------------------------------
; 1ms delay
;	This is used to make the packet intervals
;------------------------------------------------------------------------
delay1ms
	bcf	INTCON, T0IF	;clear overflow flag
	movlw	.130		;new timer base
	addwf	TMR0, f		;add to timer

d11	btfss	INTCON,	T0IF	;1ms gone?
	goto	d11		;not yet

	return



;------------------------------------------------------------------------
; MAIN PROGRAMME
;	This will first increment count_0, read speed setting
;	buttons (to adjust potvalue) and then -- in case speed value
;	has been changed (compared to oldpot) or other buttons are 
;	pressed or in case something has changed recently -- send 
;	appropriate packet. Main programme keeps the nulcount counter. 
;------------------------------------------------------------------------
main
	call 	init		;intialize ports
m0
	call 	IncCounters	;

	call	read_sw		;read the accel/brake buttons (switch)
				;to potvalue

m1	comf	PORTB, w	;copy and invert inputs to w
	andlw	b'00111111'	;mask address bytes away to see
	skpz			;if any buttons pressed?
	goto 	m2		;yes, out with it!
	tstf	nulcount	;none! Test if all null packets sent?
	skpnz			;well?
	goto	m0		;yes, back to square one
	decf	nulcount, f	;no, decrement nul packet count
	goto	m2nul		;and send one!

m2	movlw	.3		;prepare to send X null packets
	movwf	nulcount	;in case no buttons pressed

m2nul
	;----------------------------------------------------------
	; Prepare to send packet by setting the data into DATA1:
	;
	; 	"AAWDSSSS"
	;		"AA"  =Address
	;		"W"   =0 speed+dir
	;		      =1 F buttons
	;		"D"   =direction button
	;		       (or future channel select)
	;		"SSSS"=Speed from potvalue
	;		       or F0...FL
	;----------------------------------------------------------

	clrf	DATA1		;clear byte to be sent

m2fl

	btfsc	FL_BUTTON	;Check FL button is pressed?
	goto	m2f1		;no, goto test F1 instead
	movlw	b'00100101'	;yes, W=1, D=0, SSSS=.5
	movwf	DATA1		;and the lot to DATA1
	goto	m2addr		;and go to settle address bits

m2f1
	btfsc	F1_BUTTON	;Check if F1 button is pressed?
	goto	m2ch		;no, goto channel switch test
	movlw	b'00100001'	;yes, W=1, D=0, SSSS=.1
	movwf	DATA1		;and the lot to DATA1
	goto	m2addr		;and go to settle address bits

m2ch
	btfsc	CH_SWITCH	;Check if CHANNEL switch is on?
	goto	m2spd		;no, goto vanilla speed+dir
	movlw	b'00110000'	;yes, W=1, D=0, SSSS= from pot (see "vanilla")
	movwf	DATA1		;and the lot to DATA1

m2spd
	;-------------------------------------------------------------
	;Ordinary plain vanilla speed+dir byte
	;-------------------------------------------------------------

	btfss	DIR_BUTTON	;Check for direction button
	bsf	DATA1, 4	;set resp. bit in data1 if grounded!

	movfw	potvalue	;copy pot value to w
	andlw	0x0F		;clear top bits just to be safe
	iorwf	data1, f	;and set resp. bits in data1
m2addr
	;-------------------------------------------------------------
	;Set address bits
	;-------------------------------------------------------------
	btfss	ADDRHI		;Check for address hi bit
	bsf	DATA1, 7	;set resp. bit in data1 if grounded!

	btfss	ADDRLO		;Check for address lo bit
	bsf	DATA1, 6	;set resp. bit in data1 if grounded!


	;-------------------------------------------------------------

	call	IncCounters	;

	;-------------------------------------------------------------
	; Sending of the packet
	;
	;	The packet consists of:
	;		-1 bit preamble (always zero)
	;		-8 bit payload
	;		-1 bit parity
	;	Data to be sent is put into DATA1-register. The 
	;	DATA1-register is shifted left through carry
	;	and in case carry becomes set "one" is sent,
	;	else a "zero" is sent.
	;	Bitcount is used for counting the number of bits to
	;	rotate and send.
	;	Parity is counted by incrementing Parity counter
	;	whenever "one" is sent and the LSB of parity register
	;	is the parity proper, and that is sent after payload.
	;--------------------------------------------------------------
	clrf	Parity		;clear Parity, 1's will increment

	movlw	.8		;all 8 bits
	movwf	BITCOUNT	;

	bcf	INTCON, T0IF	;clear overflow flag
	movlw	HALFBIT		;new timer base
	movwf	TMR0		;move initial timer base (first round!)

	call	Send0		;send startbit (won't affect parity!)

m3	rlf	DATA1, F	;rotate through carry (MSB first) and 
	btfsc	STATUS, C	;in case of carry send 1 instead of 0
	goto	m4		;carry=1
	call	Send0		;carry=0
	goto	m5
m4	call	Send1

m5	decfsz	BITCOUNT, F	;all bits sent?
	goto m3			;not yet, send more

	btfsc	Parity, 0	;in case Parity 1 send 1 instead of 0
	goto	m6		;Parity=1
	call	Send0		;Parity=0
	goto	m7
m6	call	Send1

	;--------------------------------------------------------------
	; Packet interval
	;
	;	Packet interval is 40..70ms depending on address.
	;	In case addresses MSB is set, 20ms is added to
	;	packet interval, and in case LSB is set, 10ms is
	;	added
	;--------------------------------------------------------------
m7	movlw	.40		;count 40 ms
	movwf	BITCOUNT	;bitcount counts ms now

	btfss	ADDRHI		;Check for ADDRESS HI bit
	goto 	m7_		;not grounded, goto see lo bit
	movlw	.20		;was grounded, add 20 ms to loop
	addwf	BITCOUNT, f	;

m7_	btfss	ADDRLO		;Check for ADDRESS LO bit
	goto	m8		;not grounded, out with it!
	movlw	.10		;was grounded, add 10 ms to loop
	addwf	BITCOUNT, f	;
	

m8	call 	delay1ms

	decfsz	BITCOUNT, F	;packet interval gone yet?
	goto 	m8		;not yet, wait another millisec!

	goto 	m0		;done, back to square one
	end



