;************************************************************************
; TaPRK IR-LINK-TRANSMITTER POT VERSION Inverted v.4i (2004-11-07)
;************************************************************************

	LIST P = 16F84,  R = HEX		;Prosessor type, radix
	INCLUDE	"p16f84.inc"			;w,f,porta,portb etc. 
	ERRORLEVEL -224				;Prevent error message
						;from "tris"-command

        __CONFIG _PWRTE_ON & _HS_OSC & _WDT_OFF;4MHz crystal   

;************************************************************************
; SCHEMATIC DIAGRAM
;************************************************************************

;	          		  +---+_+---+
;	GROUP1		PORTA.2 <-|1      18|-> PORTA.0	IR-LED act.high
;	GROUP2		PORTA.3 <-|2      17|<- PORTA.0	DATA_OUT
;	POT&CAP		PORTA.4 <>|3      16|-- XTAL
;			    Vdd --|4      15|-- XTAL
;			Vss(GND)--|5      14|-- Vdd (+5V)
;	AddL/AddButton	PORTB.0 ->|6      13|<- PORTB.7	AddH/IDH
;	AddL/14/28Sw	PORTB.1 ->|7      12|<- PORTB.6	AddH/IDL
;	AddL/DirSw	PORTB.2 ->|8      11|<- PORTB.5	AddH/spare
;	AddL/F0Sw	PORTB.3 ->|9      10|<- PORTB.4 AddH/F1Sw	
;		    	          +---------+

;************************************************************************
; 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
;
;	[2004-11-07] Added Emg.stop if speed reduced to 
;	zero very quickly.
;
;	[2004-10-17] Converted the pot to have max.speed at 
;	low resistance -- i.e. _inverted_ the potentiometer!
;
;	[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 * ID) interval:
;	ID 0...3 -> 40...70 ms interval.
;
;	Reading the pot
;	===============	
;	Reading pot values in much the same way as PC-compatible's
;	joystick port!
;
;	1)	Make PORTA.4 output and discharge Cap through port.
;	2)	Make PORTA.4 input (schmitt!) and wait for going high
;	3)	Send number of loop counts it took to go high
;
;	More about PC-joystick port, see:
;	http://www.epanorama.net/documents/joystick/pc_joystick.html
;
;	Datasheet states max. capacitance to be connected
;	to any 16F84-port as 50 pF. Used 120pF and 1M +100K
;
;	Time constant: T(63%)=RC close to schmitt trip level
;
;	Connecting Cap from PORTA.4 to gnd, and Pot through
;	22k resistor from Vhi to PORTA.4
;
;	Looping through counter takes approx 4 inst.cycles (4us)
;	so min. time (count 1) should be about 4us,
;	and max. time (count 255) about 1ms.
;
;************************************************************************
; VARIABLES
;************************************************************************

	CBLOCK H'0C'

		Data1		;Data from IR-receiver
		Parity		;Parity
		bitcount	;bit counter
		potvalue	;pot position
		potbase		;pot start position (for auto null)
		bcd		;BCD_to_bin
		temp		;BCD_to_bin

		e_stop		;e_stop status
		speed
		ex_speed
		s_count

		addr
		ex_addr
		a_count

		func
		ex_func
		f_count

		payload
	ENDC
;************************************************************************
; STATICS
;************************************************************************


	#DEFINE DATA_OUT	PORTA, 0 	;Data out (if IR fails)
	#DEFINE IRLED		PORTA, 1 	;IR LED
	#DEFINE GROUP1		PORTA, 2 	;GROUP1, Address switch
	#DEFINE GROUP2		PORTA, 3 	;GROUP2, other switches
	#DEFINE POT		PORTA, 4 	;potentiometer

	#DEFINE BUTTON		PORTB, 0 	;Read address switches
	#DEFINE S_MODE		PORTB, 1 	;Address mode: 14/28 steps
	#DEFINE DirSw		PORTB, 2 	;Direction switch
	#DEFINE F0Sw		PORTB, 3 	;F0/FL switch
	#DEFINE F1Sw		PORTB, 4 	;F1    switch

	#DEFINE ID_HI		PORTB, 7 	;IR ID High bit
	#DEFINE ID_LO		PORTB, 6 	;IR ID Low  bit

	#DEFINE HALFBIT		.203 		;= 255-52
	#DEFINE QUADBIT		.229		;= 255-26

	constant MAXSPEED	=.31	 	;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'00010000'	;RA4  =in	CAP&POT
	tris	porta
	movlw	b'11111111'	;
	tris	portb

	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]

ramdone

	movlw	b'00000000'	;initiate address byte
	movwf	addr
	movlw	b'11000000'	;initiate speed byte
	movwf	speed
	movlw	b'10000000'	;initiate func byte
	movwf	func

	call	read_pot	;Read the pot for the first time
	movfw	potvalue	;and use the reading to set the
	movwf	potbase		;zero point for the pot

	return

;------------------------------------------------------------------------
; BCD to BIN [ from www.piclist.com (Scott Dattalo) ]
;------------------------------------------------------------------------

BCD_to_BIN 			;
	movwf	bcd		;
	rrf	bcd, W 
	andlw	b'01111000'	;W = tens*8 
	movwf	temp 
	clrc
	rrf	temp, F		;temp = tens*4 
	rrf	temp, F		;temp = tens*2 
	subwf	bcd, W		;W = tens*16 + ones - tens*8 
				;W = tens*8 + ones 
	addwf	temp, W		;W = tens*10 + ones 
	return

;------------------------------------------------------------------------
; Send BYTE Data to be sent in DATA1, number of bits to sed at BITCOUNT
;------------------------------------------------------------------------
send_byte

sb3	rlf	DATA1, F	;rotate through carry (MSB first) and 
	btfsc	STATUS, C	;in case of carry send 1 instead of 0
	goto	sb4		;carry=1
	call	Send0		;carry=0
	goto	sb5
sb4	call	Send1

sb5	decfsz	BITCOUNT, F	;all bits sent?
	goto sb3		;not yet, send more
	return

;------------------------------------------------------------------------
; Send 0 [###___] leaves TMR to falf bit interval
;------------------------------------------------------------------------
send0
	bsf	DATA_OUT	;direct data out
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

	bcf	INTCON, T0IF	;clear overflow flag
	movlw	HALFBIT		;new timer base
	addwf	TMR0, f		;add to just overflowed timer

s02	bcf	DATA_OUT	;direct link
	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 [___###]
;------------------------------------------------------------------------
send1
	bcf	DATA_OUT	;direct link off
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
	bsf	DATA_OUT	;direct link on
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

	bcf	INTCON, T0IF	;clear overflow flag
	movlw	HALFBIT		;new timer base
	addwf	TMR0, f		;add to just overflowed timer
	bcf	DATA_OUT	;direct link off
	return
;------------------------------------------------------------------------
;Read pot
;------------------------------------------------------------------------
read_pot
	bsf	STATUS, RP0	;Select Bank1 and
	bcf	TRISA, 4	;choose RA4 -> output
	bcf	STATUS, RP0	;Select Bank0 

	bcf	POT		;discharge cap by grounding
	nop			;RA4 and keep it down
	nop			;for a while in case of
	nop			;current limiting resistor

	clrf	potvalue	;clear previous pot position
	bsf	STATUS, RP0	;Select Bank1 and choose
	bsf	TRISA, 4	;RA4 -> input for reading cap
	bcf	STATUS, RP0	;Select Bank0


	movlw	.5		;This is a pulling procedure:
	movwf	potvalue	;The cap (due to high R and low C
null_pot			;start to charge slowly
	decfsz	potvalue, f	;we'll kill time here
	goto	null_pot	;to make sure we get zero as well

test_pot
	btfsc	POT		;test if cap charged
	return			;yes, done!
	incf	potvalue, f	;no, increment counter
	goto	test_pot	;and test again
	return

;Faster pot test routine from Scott Dattalo
;[http://home.clear.net.nz/pages/joecolquitt/0pots.html]:
;
;	decf	potvalue, f	;goto FF to get zero ;)
;test_pot
;	incf   potvalue,f
;	btfss  POT		;bump measure while pot pin is low
;	goto   test_pot		;else jump out and store count
;	return


;------------------------------------------------------------------------
; 1ms delay
;------------------------------------------------------------------------

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
;------------------------------------------------------------------------
main
	call 	init		;intialize ports
m0

;	movfw	addr		;save ex-value for comparison
;	movwf	ex_addr		;with the value we'll get soon
	movlw	b'00000000'	;modify new to resemble barebone
	movwf	addr		;IR address payload: 0AAAAAAA

;	movfw	speed		;save ex-value for comparison
;	movwf	ex_speed	;with the value we'll get soon
	movlw	b'11000000'	;modify new to resemble barebone
	movwf	speed		;IR speed payload: 11DsSSSS

;	movfw	func		;and function packet!
;	movwf	ex_func		;with the value we'll get soon
	movlw	b'10000000'	;modify new to resemble barebone
	movwf	func		;IR func payload: 10MFFFFF

	;----------------------------------------------------------------
	;First get the speed settings
	;----------------------------------------------------------------

	call	read_pot	;read the pot value
				;to potvalue
	movfw	potvalue	;get potvalue to w
	subwf	potbase, w	;base-pot (="invert" pot values)
	btfss	status, c	;Did it go below zero c=0(too slow)?
	movlw	.0		;yes, set w to zero
	xorlw	.0		;get flags from w
				;-- skip other zero and Emg.stop --
	skpz			;well, was it zero then?
	addlw	.3		;no, add 3 to skip other zero
				;and emergency stop positions
				;-----------------------------------

	movwf	potvalue	;move result to potvalue

	sublw	MAXSPEED	;max-pot (now testing for speeding!)
	btfsc	status, c	;Did it go below zero c=0(too fast)?
	goto	read_addr	;no, continue
	movlw	MAXSPEED	;yes, replace w. MAXSPEED and
	movwf	potvalue	;move it to potvalue

read_addr

	;----------------------------------------------------------------
	;Then check the addr_change button
	;----------------------------------------------------------------
	bcf	GROUP1		;now reading (grounding) other switches
	bsf	GROUP2		;not reading (releasing) address sw's
	nop
	nop
	btfsc	BUTTON		;
	goto	read_sw		;not pressed (clear) goto switches

	tstf	potvalue	;Check the speed setting
	skpz			;it must be zero before changing address
	goto	read_sw		;not zero, goto switches

	bsf	GROUP1		;not reading (releasing) other switches
	bcf	GROUP2		;now reading (grounding) address sw's
	comf	portb, w	;read address switches

	bcf	GROUP1		;now reading (grounding) other switches
	bsf	GROUP2		;not reading (releasing) address sw's
	nop
	nop
	btfsc	BUTTON		;now read the switches, is the button still down
	goto	read_sw		;not pressed (clear) goto switches

	call	bcd_to_bin	;convert to binary address
	andlw	b'01111111'	;just to be sure...
	movwf	addr		;and save as new address
	movwf 	payload
;	movlw	.10		;repeat for address
;	movwf	a_count		;set as a dummy really...
	goto	send_packet	;and send it!

read_sw
	;----------------------------------------------------------------
	;Go to read other switches
	;----------------------------------------------------------------

	bcf	GROUP1		;now reading (grounding) other switches
	bsf	GROUP2		;not reading (releasing) address sw's
	nop
	nop

	;----------------------------------------------------------------
	;First check the dir switch and set/clear speed's dir bit
	;----------------------------------------------------------------
	
	btfss	DirSw		;Dir reverse (set) / forward (clear)
	bsf	speed, 5

	;----------------------------------------------------------------
	;Next check the F1 and set/clear func's F1 bit
	;----------------------------------------------------------------

	btfss	F1Sw		;F1 off (set) / on (clear)
	bsf	func, 0

	;----------------------------------------------------------------
	;Next check the mode switch and clear/set flag  bit in func byte,
	;set correct bit according to FL/F0func byte and handle speed
	;and functions:
	;----------------------------------------------------------------

	btfss	S_MODE		;Speed in 14 (set) / 28 (clear)
	goto	m_28
m_14
	;----------------------------------------------------------------
	; 14 step mode:
	; -------------
	;	Speed byte:	11DsSSSS  D    = Dir 1=Fwd, 0=Rev
	;				  s    = F0/FL,
	;				  S..S = Speed bits 3..0
	;	Func byte:	10M0FFFF  M    = 0 (14 step mode)
	;				  F..F = F4..1 
	;----------------------------------------------------------------

	bcf	func, 5		;speed_mode flag cleared (14 steps)
	bcf	speed, 4	;clear the little "s"
	btfss	F0Sw		;FL off (set) / on (clear)
	bsf	speed, 4

	rrf	potvalue,w	;get pot value, LSB to carry (discard!)
	andlw	0x0F		;clear top (just in case!)
	iorwf	speed, f	;
	goto	m1		;and done here!

m_28
	;----------------------------------------------------------------
	; 28 step mode:
	; -------------
	;	Speed byte:	11DsSSSS  D    = Dir 1=Fwd, 0=Rev
	;				  s    = speed bit 0
	;				  S..S = Speed bits 4..1
	;	Func byte:	10MFFFFF  M    = 1 (28 step mode)
	;		 		  F..F = F0,F4..F1 
	;----------------------------------------------------------------

	bsf	func, 5		;speed_mode flag set (28 steps)
	btfss	F0Sw		;FL off (set) / on (clear)
	bsf	func, 4
	bcf	speed, 4
	rrf	potvalue,w	;get pot value, LSB in carry
	skpnc			;was carry set
	bsf	speed, 4	;yes, set little "s"
	andlw	0x1F		;clear top (just in case!)
	iorwf	speed, f	;

m1

send_speed
	;----------------------------------------------------------------
	;Now, lets see if speed needs sending
	;----------------------------------------------------------------
	tstf	e_stop		;Do we have an emergency?
	skpz			;
	goto	es_on		;yes, lets act upon it!
	movfw	ex_speed	;Load old speed to w
	xorwf	speed, w	;and compare with latest one.
	btfsc	status, z	;Was it equal?
	goto	send_sp1	;yes, check for repeats!

	movfw	speed		;Test new speed:
	andlw	b'00001111'	;clear all but 4 speed bits
	skpz			;Is it zero
	goto	send_sp0	;no, go on
	movfw	ex_speed	;yes, how about previous?
	andlw	b'00001100'	;was it over 3/14 (6/28)
	skpnz			
	goto	send_sp0	;no, go on
	movfw	speed		;make sure the staus will not 
	movwf	ex_speed	;stay on for ever
	movlw	.10
	movwf	e_stop		;set the e_state (10 repeats)

es_on
	decf	e_stop, f	;reduce the e_stop counter
	movfw	speed
	andlw	b'11110000'	;get the FL and direction
	movwf	payload		;get this to payload
	bsf	payload, 0	;set bit 0 (emg stop!)	
	movlw	b'11000001'	;this is emergency packet
	goto	send_packet

send_sp0
	movlw	.10		;no, lets make sure
	movwf	s_count		;this is noticed
	movfw	speed		;get speed to w
	movwf	payload		;and prepare to send it
	movwf	ex_speed	;and make ex_speed equal to present
	goto	send_packet

send_sp1
	tstf	s_count		;test if repeats needs to be sent?
	skpnz
	goto	send_func	;no, check func next!
	decf	s_count,f	;yes, decrement repeat counter
	movfw	speed		;get speed and
	movwf	payload		;and prepare to send
	goto	send_packet	;go and send it!

send_func
	;----------------------------------------------------------------
	;Now, lets see if func needs sending
	;----------------------------------------------------------------

	movfw	ex_func		;Load old func to w
	xorwf	func, w		;and compare with latest one.
	btfsc	status, z	;Was it equal?
	goto	send_f1		;yes, how about repeats?

	movlw	.10		;no, lets make sure
	movwf	f_count		;this is noticed
	movfw	func		;get func to w
	movwf	payload		;and prepare to send it
	movwf	ex_func		;and make ex_func equal to present
	goto	send_packet

send_f1
	tstf	f_count		;test if repeats needs to be sent?
	skpnz
	goto	m0		;no, back to square one!
	decf	f_count, f	;yes, decrement repeat counter
	movfw	func		;get func packet and
	movwf	payload		;and prepare to send
	goto	send_packet	;go and send it!


send_packet

	;----------------------------------------------------------
	;send startbit
	;----------------------------------------------------------

	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!)

	;----------------------------------------------------------
	;send cab number
	;----------------------------------------------------------

	bcf	GROUP1		;now reading (grounding) other switches
	bsf	GROUP2		;not reading (releasing) address sw's
	
	clrf	Parity		;clear Parity, 1's will increment
	clrf	DATA1		;clear DATA1

	btfss	ID_HI		;Check for address hi bit
	bsf	DATA1, 7	;set resp. bit in data1 if grounded!

	btfss	ID_LO		;Check for address lo bit
	bsf	DATA1, 6	;set resp. bit in data1 if grounded!

	movlw	.2		;only 2 bits
	movwf	BITCOUNT	;

	call 	send_byte	;send it along!

	;----------------------------------------------------------
	;send payload
	;----------------------------------------------------------

	movfw	payload		;get the payload
	movwf	DATA1		;
	movlw	.8		;all 8 bits
	movwf	BITCOUNT	;

	call send_byte		;send DATA1

	;----------------------------------------------------------
	;send parity
	;----------------------------------------------------------

	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

m7	movlw	.40		;count 40 ms
	movwf	BITCOUNT	;bitcount counts ms now

	btfss	ID_HI		;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	;

	;----------------------------------------------------------
	;set packet interval
	;----------------------------------------------------------

m7_	btfss	ID_LO		;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

