;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;; C O N F I G ;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    include "p12f629.inc"

    __config _PWRTE_OFF & _WDT_OFF & _MCLRE_OFF & _BODEN_OFF & _INTRC_OSC_NOCLKOUT        
    
 	errorlevel    -302

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;; V A R I A B L E S ;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    cblock 0x20
	d1				;registry na countery pro 1s delay
	d2
	d3
	c1				;kolik preruseni na pulperiodu
	c2
	c3
	running			;TRUE=jede semafor R-Y-G,FALSE=blika Y
	range_bh		;zacatek rozsahu Hi byte
	range_bl		;zacatek rozsahu Lo byte
	range_eh		;konec rozsahu Hi byte
	range_el		;konec rozsahu Lo byte
	range_ch		;test hodnota Hi
	range_cl		;test hodnota Lo
	isPulseStart	;zacatek pulsu
	pulse_bh		;pulse start Hi (Timer1 H)
	pulse_bl		;pulse start Lo (Timer1 L)	
	pulse_eh		;pulse end Hi (Timer1 H)
	pulse_el		;pulse end Lo (Timer1 L)
	act_val			;aktualni hodnota (lo nebo hi)
	w_temp
	status_temp
	expectedPulse	;kterou cast IR signalu ocekavame
	isHalfPeriod	;Mame cekat prvni pulperiodu (bitu)?
	bitCount		;ktery bit prijimame
	IR_ADDR			;tady nakopiruju nactenou adresu
	IR_ADDR_CMP		;zde nacteny komplement adresy
	IR_CMD			;nacteny command
	IR_CMD_CMP		;komplement commandu
	tmpByte			;uloziste prijimanych bitu
	bits			;pocitadlo
	tmp
	tmp_h			;kopie range_ch
	tmp_l			;kopie range_cl
	i				;pocitadlo 
	j				;pocitadlo
	GPIO_latch		;pro posilani na vystup
    endc

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;; C O N S T A N T S ;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

EXP_AGC_BURST	equ .1	;prvnich 9ms lead signal
EXP_SPACE		equ .2	;dalsich 4.5ms lead signalu
EXP_BITSTART	equ .3	;prvni pulperioda bitu (<560us)
EXP_BITEND		equ .4	;druha pulperioda bitu (560=0,1690us=1)
EXP_STOPBIT		equ .5	;stop bit
NUM_BITS		equ .32 ;celkem 32 bitu na IR packet

;IR Adresa a kod tlacitek (NEC protocol)
ADDR			equ b'00000101'		;adress=0x05

CMD_BUTTON_1	equ b'00001001'		;command=0x09
CMD_POWER		equ b'00000001'		;command=0x01

TRUE			equ .1
FALSE			equ .0

RED				equ b'00010000'
ORANGE			equ b'00000100'
RED_ORANGE		equ b'00010100'
GREEN			equ b'00000010'


;LEDky
#define LED_RED		GPIO, GP4
#define LED_ORANGE	GPIO, GP2
#define LED_GREEN	GPIO, GP1

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;; M A C R O S ;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;Cekej 10 * 50ms = 0.5 sec
;Po kazdych 50ms zjisti, jestli se nezmenil stav semaforu na ON
OFF_CHECK macro
	local OFF_CHECK_L
	local OFF_continue

	movlw .10
	movwf i
OFF_CHECK_L
	call Pause
	decfsz running, w
	goto OFF_continue
	goto semafor_ON
OFF_continue	
	decfsz i, f
	goto OFF_CHECK_L
	endm

;Cekej 20 * 50ms = 1 sec
;Po kazdych 50ms zjisti, jestli se nezmenil stav semaforu na OFF
ON_CHECK macro
	local ON_CHECK_L

	movlw .20
	movwf i
ON_CHECK_L
	call Pause
	decfsz running, w
	goto semafor_OFF	
	decfsz i, f
	goto ON_CHECK_L
	endm

BANK0 macro
    bcf STATUS, RP0 ;Select memory bank 0
    endm
    
BANK1 macro
    bsf STATUS, RP0 ;Select memory bank 1
    endm 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
	org 0x00
	goto main
	
	org 0x04
	goto interrupt

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;; I N T E R R U P T ;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

interrupt
	movwf w_temp 		;copy W to temp register, could be in either bank
	swapf STATUS,w 		;swap status to be saved into W
	movwf status_temp 	;save status to bank 0 register

ISR
	btfss PIR1, TMR1IF
	goto IOC_ISR
	bcf PIR1, TMR1IF	;vycisti Interrupt flag pro Timer1
IOC_ISR
	btfss INTCON, GPIF
	goto skip

	movf GPIO, w		;Nutnost, jinak se bude opakovat IOC interrupt
	movwf act_val		;Uloz, co jsme zrovna nacetli za hodnotu
	bcf INTCON, GPIF	;vycisti IOC

	decfsz isPulseStart, w
	goto pulseEnd
pulseStart
	clrf TMR1H
	clrf TMR1L
	goto pulseSwitch
pulseEnd	
	movf TMR1H, w		;Konec pulzu
	movwf range_ch
	movf TMR1L, w
	movwf range_cl
	clrf TMR1H
	clrf TMR1L
pulseCommon
		call setExpRanges	;Podle stavu nastav ocekavanou sirku pulzu

		movf expectedPulse, w
		movwf tmp
pexp1	decfsz tmp, f 				;1) 9ms AGC burst
		goto pexp2
		call isInRange
		movlw EXP_AGC_BURST			;goto by nemelo hodnotu
		btfsc STATUS, C
		goto clearIOC				;pulse neni AGC, cekame na dalsi
		movlw EXP_SPACE				;pulse vyhovuje, jdi na dalsi typ
		goto clearIOC

pexp2	decfsz tmp, f				;2) 4.5ms SPACE pulse
		goto pexp3
		call isInRange
		btfsc STATUS, C
		goto badOne					;Delka pulzu nevyhovuje
		
		;Priprav data pro prijem bitu
		clrf IR_CMD_CMP				;vycisti IR packet
		clrf IR_CMD
		clrf IR_ADDR_CMP
		clrf IR_ADDR

		movlw NUM_BITS				;cekame 4B
		movwf bitCount
		movlw EXP_BITSTART
		goto clearIOC	  

pexp3	decfsz tmp, f				;3) pulse s prvni pulperiodou bitu
		goto pexp4
		
		call isInRange
		btfsc STATUS, C
		goto badOne		
		movlw EXP_BITEND
		goto clearIOC

pexp4	decfsz tmp, f				;4) pulse s druhou pulperiodou
		goto pexp5

		movf range_ch, w			;budeme testovat mozna 2x
		movwf tmp_h
		movf range_cl, w
		movwf tmp_l

		call isInRange
		btfsc STATUS, C
		goto pexp4b					;0 a 1 se lisi delkou druhe pulperiody
		bcf STATUS, C 				;mame bit 0
chkbits	rrf IR_CMD_CMP, f			;posunuj se v ramci vsech 4 bajtu
		rrf IR_CMD, f				;pres Carry se bity dostanou do
		rrf IR_ADDR_CMP, f			;dalsich bajtu IR packetu
		rrf IR_ADDR, f				;menime MSB na 0.bit
		
		decfsz bitCount, f			;mame prijaty vsechny bity?
		goto pexp4n					;cekej na dalsi bity, EXP_BITSTART							
		movlw EXP_STOPBIT			;mame vsechny bity, uz jen STOP BIT
		goto clearIOC
pexp4b	movf tmp_h, w				;potrebovali jsme kopii
		movwf range_ch
		movf tmp_l, w
		movwf range_cl
		call EXP_BITEND_1_INIT		;nutno zresetovat interval
		call isInRange				;testuj, nemame-li pulse s 1
		btfsc STATUS, C
		goto badOne
		bsf STATUS, C				;mame bit 1
		goto chkbits
		
pexp4n	movlw EXP_BITSTART
		goto clearIOC

pexp5	call isInRange
		btfsc STATUS, C
		goto badOne
		
		;Zkontroluj packet
		movf IR_ADDR, w
		xorlw ADDR
		btfss STATUS, Z		
		goto badOne					;Nesedi adresa (jiny ovladac?)
		movf IR_CMD, w
		xorlw CMD_POWER
		btfss STATUS, Z
		goto badOne
		movlw .1					;Prepni zapnuto<->vypnuto
		xorwf running, f
		goto packetOK

pulseSwitch
		movlw FALSE			;vsechny dalsi pulzy jsou konce
		movwf isPulseStart
		goto skipIOC

packetOK
badOne	movlw EXP_AGC_BURST	;pulse nevyhovuje, cekej na AGC
clearIOC	
		movwf expectedPulse

skipIOC	

skip
	swapf status_temp,w	;swap status_temp register into W, sets bank to original state
	movwf STATUS 		;move W into STATUS register
	swapf w_temp,f 		;swap w_temp
	swapf w_temp,w 		;swap w_temp into W		
	retfie
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;; M A I N ;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
main	
	clrwdt

	;Inicializace vystupnich GPIO	
	BANK1	
	call 3FFh 			;Get the cal value
	movwf OSCCAL 		;Calibrate
	
	movlw b'00100000'	;Port GP5 vstupni pro IR        
	movwf TRISIO    
	BANK0 	
	clrf GPIO 	
	movlw 07h	  
	movwf CMCON	    	;Vypni komparator
	
	BANK1
	movlw b'00100000' 	;Interrupt-on change na GP5
	movwf IOC

	movlw b'11001000'   ;Interni oscilator, prescaler 1:1 (PSA = 1)
	movwf OPTION_REG    

	bsf PIE1, TMR1IE	;Enables the TMR1 overflow interrupt

	BANK0
	bsf INTCON, GPIE    ;Povol Interrupt-on-change (GP5)
	bsf INTCON, PEIE	;Peripheral interrupt pro Timer1
	bsf INTCON, GIE	    ;Povol vsechna preruseni	

	clrf TMR1H
	clrf TMR1L
	movlw b'00000001'	;TMR1ON = zapni
	movwf T1CON

	movlw TRUE
	movwf isPulseStart	

	movlw EXP_AGC_BURST
	movwf expectedPulse

	movlw NUM_BITS		;cekame 4B (32 bitu)
	movwf bitCount

	movlw FALSE			;semafor jen blika zlute
	movwf running

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;; M A I N   L O O P ;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

loop		decfsz running, w
			goto semafor_OFF
			goto semafor_ON

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

semafor_OFF
	clrf GPIO
loop_OFF
	movlw ORANGE
	movwf GPIO
	;bsf LED_ORANGE   
	OFF_CHECK
	clrf GPIO
	;bcf LED_ORANGE   
	OFF_CHECK
	goto loop_OFF


semafor_ON
	clrf GPIO
loop_ON
	movlw RED
	movwf GPIO
	;bsf LED_RED		; Zapni cervenou	
	ON_CHECK
	ON_CHECK
	ON_CHECK
	ON_CHECK
	ON_CHECK
	movlw RED_ORANGE
	movwf GPIO
	;bsf LED_ORANGE  ; Cervena + zluta
	ON_CHECK
	ON_CHECK
	ON_CHECK
	movlw GREEN
	movwf GPIO
	;bcf LED_RED	    ; Vypni cervenou
	;bcf LED_ORANGE  ; Vypni zlutou
	;bsf LED_GREEN   ; Zapni zelenou
	ON_CHECK
	ON_CHECK
	ON_CHECK
	ON_CHECK
	ON_CHECK
	movlw ORANGE
	movwf GPIO
	;bcf LED_GREEN   ; Vypni zelenou
	;bsf LED_ORANGE  ; Zapni zlutou
	ON_CHECK
	ON_CHECK
	ON_CHECK	
	clrf GPIO
	;bcf LED_ORANGE	; Vypni zlutou	
	goto loop_ON
			
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;; D E F I N I C E  Š Í Ø E K  P U L Z Ù ;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			
;Nastav podle EXP_* stavu spravny interval sirky pulzu
setExpRanges
			movf expectedPulse, w
			movwf tmp

exp1		decfsz tmp, f
			goto exp2
			call EXP_AGC_BURST_INIT
			return
exp2		decfsz tmp, f
			goto exp3
			call EXP_SPACE_INIT
			movlw TRUE				;Ted cekame prvni pulku periody bitu
			movwf isHalfPeriod			
			return
exp3		decfsz tmp, f
			goto exp4
			call EXP_BITSTART_INIT
			movlw FALSE
			movwf isHalfPeriod		;Cekame druhou pulperiodu bitu
			return
exp4		decfsz tmp, f
			goto exp5
			call EXP_BITEND_0_INIT	;pro 0, 1 se nastavuje zvlast
			return
exp5		call EXP_STOPBIT_INIT
			return

;prvnich 9ms lead signal (8900-9100us)
EXP_AGC_BURST_INIT
			movlw .34
			movwf range_bh
			movlw .196
			movwf range_bl

			movlw .35
			movwf range_eh
			movlw .140
			movwf range_el
			return

;dalsich 4.5ms lead signalu (4400-4600us)
EXP_SPACE_INIT		
			movlw .17
			movwf range_bh
			movlw .48
			movwf range_bl

			movlw .17
			movwf range_eh
			movlw .248
			movwf range_el
			return

;prvni pulperioda bitu (470-670us)
EXP_BITSTART_INIT
			movlw .1
			movwf range_bh
			movlw .214
			movwf range_bl

			movlw .2
			movwf range_eh
			movlw .158
			movwf range_el
			return

;druha pulperioda bitu pro log. 0 (470-670us)
EXP_BITEND_0_INIT	
			movlw .1
			movwf range_bh
			movlw .214
			movwf range_bl

			movlw .2
			movwf range_eh
			movlw .158
			movwf range_el
			return	

;druha pulperioda bitu pro log. 1 (1200-1950us)
EXP_BITEND_1_INIT
			movlw .4
			movwf range_bh
			movlw .176
			movwf range_bl

			movlw .7
			movwf range_eh
			movlw .158
			movwf range_el
			return

;stop bit (470-670us)
EXP_STOPBIT_INIT
			movlw .1
			movwf range_bh
			movlw .214
			movwf range_bl

			movlw .2
			movwf range_eh
			movlw .158
			movwf range_el
			return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;; R U T I N Y ;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;Rutina testujici, zda 16bitova test hodnota range_c lezi v
;intervalu <range_b-range_e>

;Vraci nastaveny Carry, pokud NE 

;	range_bh	;zacatek rozsahu Hi byte
;	range_bl	;zacatek rozsahu Lo byte
;	range_eh	;konec rozsahu Hi byte
;	range_el	;konec rozsahu Lo byte
;	range_ch	;test hodnota Hi
;	range_cl	;test hodnota Lo
isInRange

testLow
	movf range_ch, w
	subwf range_bh, w
	btfsc STATUS, Z		;range_ch==range_bh, jdem na horni mez
	goto testLow2
	btfsc STATUS, C		;pod dolni mezi intervalu (HI), hotovo
	return
	goto testHigh
testLow2
	movf range_cl, w
	subwf range_bl, w
	btfsc STATUS, Z
	goto testHigh
	btfsc STATUS, C		;pod dolni mezi intervalu (LO), hotovo
	return
testHigh
	movf range_eh, w
	subwf range_ch, w
	btfsc STATUS, Z
	goto testHigh2
	btfsc STATUS, C
	return				;range_ch < range_eh, jsme urcite v intervalu	
	goto tend
testHigh2
	movf range_el, w
	subwf range_cl, w
	btfsc STATUS, Z
	goto tend
	btfsc STATUS, C
	return
tend
	addlw .0			;clear Carry
	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Delay = 0.05 seconds = 50ms
; Clock frequency = 4 MHz

; Actual delay = 0.05 seconds = 50000 cycles
; Error = 0 %

Pause
			;49993 cycles
	movlw	0x0E
	movwf	d1
	movlw	0x28
	movwf	d2
Pause_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	Pause_0

			;3 cycles
	goto	$+1
	nop

			;4 cycles (including call)
	return	
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	end