

;*********************************
;Copyright(C)2002,2003, 2004, 2008 by Richard Cappels projects@cappels.org
;http://www.projects.cappels.org
;
;Uses ATTINY2313 operating at 4 MHz.
;
;
;********************************
;
;
; The purpose of this code is to simulate a Princeton Technology Corp. PT2264 with a single fixed address and data.
; In the PT2264 datashet, time is denoted in clock periods of the PT2264's oscillator. These clock periods are
; referred to with the Greek leter alpha. Since the  minimum time period in the coded address and data is 4 alpha,
; this code generates output data based on minimum time slices of 4 clocks. 
;
; Geneartion of the "4 clock time slice:
; The 16 bit timer, timer1 in the AT90S2313/ATTINY2313, is used what some manufacturers call the free run mode.
; A count compare register is loaded with a number which is = (crystal frequency/(PT2264 clock frequency/4),and 
; Whenever the timer counts up to that number of microcontroller clocks, the counter is reset and an interrupt 
; occurs. When the interrupts are serviced, the contents of PORTD is updated to reflect the data stored in a
; register, called dout, for that purpose. In other words, output data for PORTD is written to dout and then
; once interrupt, the data is copied from dout to PORTD. This assures that the output data is synchronous with
; the interrupts.
;
; To assure that interrupts are equally spaced, and thus there is minmum jitter in the data being sent, the
; microcontroller sleeps and waits for an interrupt after each change in the data output, This assures 
; that interrupt latency is consistent.
;
;	Note: When programming the chip, set Browout detectiion OFF, and the watchdog timer OFF to save power. 
;
;		
;
; pt2264i081004A				Verson.


.include "tn2313def.inc"

.equ    clock 		= 4000000   ; Clock frequency

.def	temp		= r16		; General purpose scratch register.
.def	intcounter	= r17		; Counter used to test interrupt timing.
.def	dout		= r18		; Data output to PORTD every T1_compare_int.
.def	delay		= r19		; Delay counter.
.def	hundelay	= r20		; Dedicated register for timing 100 millisecond delay..



;Definition of PORTB bits:
;0
;1
;2		Motorcycle_Entrance	input with pullup
;3		Test				input with pullup	
;4		Manual 				input with pullup
;5		Motorcycle_Exit		input with pullup
;6		Pedestrian Open 	input with pullup
;7		Pedestrian Close 	input with pullup




;Definition of PORTD bits:
;0		       		input with pullup
;1		       		input with pullup
;2		       		input with pullup	
;3		       		input with pullup	
;4		       		input with pullup	
;5		Data output
;6.		Data output
;7		Not present on AT90S2313/ATTINY2313


.org    $00
	rjmp	reset				; Power-on and reset vector.

.org	$04		
	rjmp	T1_compare_int		; 16 Bit timer overflow.

.org	$0B						; Pin Chance interrupt.
	rjmp	reset


T1_compare_int:					; The purpose is to wake the CPU every 301.75 us.
	out		PORTD,dout			; Update PORTD otuput pins. No flags affected by this instruction.
	reti



Sleep_now:						; Sleep until interrupt.
	sei							; Assure interrupts are enabled.
	ldi		temp,0b00100000		; Enable sleep mode (idle).
	out		MCUCR,temp
	sleep
	ret


delay_3:						; Delay 3 minimum time slices.
	ldi		delay,3
delay_3_loop: 
	rcall	Sleep_now			; Wait one minimum time slice.
	dec		delay
	brne	delay_3_loop
ret


delay_31:
	ldi		delay,31			; Delay 31 minimum time slices.
delay_31_loop: 
	rcall	Sleep_now			; Wait one minimum time slice.
	dec		delay
	brne	delay_31_loop
	ret


send_0:							; Send a logic zero. 1 interrupt high, 4 low,two times.	
	ori		dout,0b01100000		; Set bit 6 high for one minimum slice.
	rcall	Sleep_now			; Wait one minimum time slice.
	andi	dout,0b10011111		; Set bit 6 low for three minimum slices.
	rcall	delay_3
	ori		dout,0b01100000		; Set bit 6 high for one minimum slice..
	rcall	Sleep_now			; Wait one minimum time slice.
	andi	dout,0b10011111		; Set bit 6 low for three minimum slices.
	rcall	delay_3
	ret


send_1:
	ori		dout,0b01100000		; Set bit 6 high for three minimum slices.
	rcall	delay_3
	andi	dout,0b10011111		; Set bit 6 low for one minimum slice.
	rcall	Sleep_now
	ori		dout,0b01100000		; Set bit 6 high for three minimum slices.
	rcall	delay_3				
	andi	dout,0b10011111		; Set bit 6 low for one minimum slice.
	rcall	Sleep_now			; Wait one minimum time slice.
	ret


send_F: 						; 1 high, 3 low, 3 high, 1 low
	ori		dout,0b01100000		; Set bit 6 high for one minimum slice.
	rcall	Sleep_now			; Wait one minimum time slice.
	andi	dout,0b10011111		; Set bit 6 low for three minimum slices.
	rcall	delay_3
	ori		dout,0b01100000		; Set bit 6 high for three minimum slices.
	rcall	delay_3
	andi	dout,0b10011111		; Set bit 6 low for one minimum slice.
	rcall	Sleep_now
	ret


send_sync:						; 1 high, 31 low.
	ori		dout,0b01100000		; Set bit 6 high for one minimum slice.
	rcall	Sleep_now			; Wait one minimum time slice.
	andi	dout,0b10011111		; Set bit 6 low for 31 minimum slices.
	rcall	delay_31
	ret


interword_delay:				; Delay for 32 minimum time slices.
	rcall	delay_31
	rcall	Sleep_now
ret


send_interframe_delay:			; Delay by 94 minimum time slices = 442.75 us.
	rcall	delay_31
	rcall	delay_31
	rcall	delay_31
	rcall	delay_31
ret


delay_2s:						; Delay 2 seconds.
delay_2s_loop:
	rcall	send_interframe_delay
	dec		hundelay
	brne	delay_2s_loop	
	ret


send_code_word:					; Code: L	H	F	H	F	H	H	L	H	L	F	L
	rcall	send_0
	rcall	send_1
	rcall	send_F
	rcall	send_1
	rcall	send_F
	rcall	send_1
	rcall	send_1
	rcall	send_0
	rcall	send_1
	rcall	send_0
	rcall	send_F
	rcall	send_0
	rcall	send_sync
ret


send_code_frame:
	rcall	send_code_word
	rcall	send_code_word
	rcall	send_code_word
	rcall	send_code_word
ret


reset:
	cli								; Assure interrupts are off for now.     

	ldi 	temp,low(ramend)
	out 	spl,temp				; Set stack pointer.

	ldi		temp,0b00000000			; Set PORTB Data Direction Register
	out		DDRB,temp

	ldi		temp,0b11111111			; Set PORTB Data Register
	out		PORTB,temp	

	ldi		temp,0b11111111			; Set PORTD Data Direction Register
	out 	DDRD,temp

	ldi		temp,0b00000000			; Set PORTD Data Register
	out		PORTD,temp	

	ldi		dout,0b00000000			; Set update data for PORTD.

	ldi		temp,0b10000000
	out		ACSR,temp

	ldi		temp,$00				; Don't really need to do this, but it shows TCCR1A as zeros.
	out		TCCR1A,temp

	ldi		temp,$00				; Clear 16 bit counter.
	out		TCNT1H,temp
	out		TCNT1L,temp

	ldi		temp,$04				; Set value of compare match register -high register.
	out		OCR1AH,temp

	ldi		temp,$B7				; Set value of compare match register -low register..
	out		OCR1AL,temp	
	
	ldi		temp,0b00001001			; Enable input to Timer/Counter 1 (16 bit counter) clock/1.
	out		TCCR1B,temp

	ldi		temp,0b01000000			; Enable Timer 1 (16 bit timer) compare match interrupts.
	out		TIMSK,temp

	ldi		temp,0b00000000			; Disable pin change interrupts.
	out		GIMSK,temp		

	rjmp	Main					; Go off and execute the main loop.


test:								; Switch the code outputs high and keep it there until reset.
	ldi		temp,0b01100000
	out		PORTD,temp
	rjmp	test

manual:

	rcall	send_code_frame;
	rcall	send_code_frame;
	rcall	send_code_frame;
	rcall	send_code_frame

	rjmp	switch_off


switch_off:							; Stop timer 1 interrupts, make sure buttons are up, and go to Power Down mode..


	in		temp,PINB
	andi	temp,0b11111100
	subi	temp,0b11111100
	brne	switch_off

	ldi		hundelay,2				; About 27 counts per second.
	rcall	delay_2s


	in		temp,PINB
	andi	temp,0b11111100
	subi	temp,0b11111100
	brne	switch_off


	ldi		temp,0b00000000			; Disable Timer 1 (16 bit timer) compare match interrupts.
	out		TIMSK,temp

	ldi		temp,0b00100000			; Enable pin change interrupts.
	out		GIMSK,temp		

	ldi		temp,0b00100000		
	out		EIFR,temp

	ldi		temp,0b11111100			; Enable interrupts from bits on PORTB.
	out		PCMSK,temp

	sei								; Assure interrupts are enabled.
	ldi		temp,0b01110000			; Enable Power Down sleep mode.
	out		MCUCR,temp
	sleep							; After restart, the program runs to here, but no interrputs.


motorcycle_exit:					; Open the gate for 30 seconds, then close it, then shut down..	
	rcall	send_code_frame;
	rcall	send_code_frame;
	rcall	send_code_frame;
	rcall	send_code_frame;

	ldi		hundelay,255			; Delay about 40 seconds.
	rcall	delay_2s
	ldi		hundelay,255			; About 27 counts per second. For 5 seconds, use 135
	rcall	delay_2s
	ldi		hundelay,255			; About 27 counts per second. For 5 seconds, use 135
	rcall	delay_2s
	ldi		hundelay,100			; About 27 counts per second. For 5 seconds, use 135
	rcall	delay_2s

	rcall	send_code_frame;
	rcall	send_code_frame;
	rcall	send_code_frame;
	rcall	send_code_frame

	rjmp	switch_off


motorcycle_enter:					; Open the gate for 30 seconds, then close it, then shut down..	

	rcall	send_code_frame;		; First series of commands starts to open again 
	rcall	send_code_frame;		; The motorcyle enters while gate is moving.
	rcall	send_code_frame;
	rcall	send_code_frame;

	ldi		hundelay,255			; Delay about 1 1/2 seconds.
	rcall	delay_2s

	rcall	send_code_frame;		; Second series of command pulses stops the gate
	rcall	send_code_frame;	
	rcall	send_code_frame;
	rcall	send_code_frame

	ldi		hundelay,55				; Delay about 2 seconds.
	rcall	delay_2s

	rcall	send_code_frame;		; Third series of command pulses closes the gate
	rcall	send_code_frame;	
	rcall	send_code_frame;
	rcall	send_code_frame

	rjmp	switch_off


Pedestrian_Close:					; Close the gate after a pedestrian has gone through. If gate is not all the way open.

	rcall	send_code_frame;		; First series of commands starts to open again 
	rcall	send_code_frame;
	rcall	send_code_frame;
	rcall	send_code_frame;

	ldi		hundelay,40				; Delay about 1 1/2 seconds.
	rcall	delay_2s

	rcall	send_code_frame;		; Second series of command pulses stops the gate
	rcall	send_code_frame;	
	rcall	send_code_frame;
	rcall	send_code_frame

	ldi		hundelay,55				; Delay about 2 seconds.
	rcall	delay_2s

	rcall	send_code_frame;		; Third series of command pulses closes the gate
	rcall	send_code_frame;	
	rcall	send_code_frame;
	rcall	send_code_frame

	rjmp	switch_off


Pedestrian_Open:					; Close the gate after a pedestrian has gone through. If gate is not all the way open.

	rcall	send_code_frame;		; First series of commands starts to open again 
	rcall	send_code_frame;
	rcall	send_code_frame;
	rcall	send_code_frame;

	ldi		hundelay,165			; Delay about 6 seconds.
	rcall	delay_2s

	rcall	send_code_frame;		; Second series of command pulses stops the gate
	rcall	send_code_frame;	
	rcall	send_code_frame;
	rcall	send_code_frame

	rjmp	switch_off



Main:								; Program's main loop.

	sei								; Allow interrupts so the delay routine canwork.
	ldi		hundelay,6				; About 27 counts per second. For 5 seconds, use 135
	rcall	delay_2s

	sbis	PINB,7
	rjmp	Pedestrian_Close

	sbis	PINB,6
	rjmp	Pedestrian_Open

	sbis	PINB,5
	rjmp	motorcycle_exit

	sbis	PINB,4
	rjmp	manual

	sbis	PINB,3
	rjmp	test

	sbis	PINB,2
	rjmp	motorcycle_enter
	
	rjmp	switch_off


.exit

	
	

