 TITLE  "CO detector"  
 List P=16F917, R=DEC
 INCLUDE "p16F917.inc"

; data segment in the shared area
 CBLOCK 0x70             	 	
 del, delTMP, temp, buzzT
 coValue, coHigh
 hundreds, tens, units, displayCodes:3
 ENDC

 PAGE
 __CONFIG _DEBUG_OFF & _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF &  _CP_OFF & _MCLRE_OFF & _PWRTE_ON  & _WDT_OFF & _INTOSCIO

  ORG    0
	goto 	main

codeUnitsR				; 7-segment encoding routines
	addwf 	PCL, f
	retlw 	b'10001110' 		; right part of digit 0
	retlw	b'00001000'		; right part of digit 1
	retlw	b'10001101'		; right part of digit 2
	retlw	b'10001101'		; right part of digit 3
	retlw	b'00001011'		; right part of digit 4
	retlw	b'10000111'		; right part of digit 5
	retlw	b'10000111'		; right part of digit 6
	retlw	b'00001100'		; right part of digit 7
	retlw	b'10001111'		; right part of digit 8
	retlw	b'10001111'		; right part of digit 9

codeUnitsL
	addwf	PCL, f
	retlw	b'10010000'		; left part of digit 0
	retlw	b'00010000'		; left part of digit 1
	retlw	b'10000000'		; left part of digit 2
	retlw	b'00010000'		; left part of digit 3
	retlw	b'00010000'		; left part of digit 4
	retlw	b'00010000'		; left part of digit 5
	retlw	b'10010000'		; left part of digit 6
	retlw	b'00010000'		; left part of digit 7 
	retlw	b'10010000'		; left part of digit 8
	retlw	b'00010000'		; left part of digit 9

codeTensR
	addwf	PCL, f
	retlw	b'00110000'		; right part of digit 0
	retlw	b'00010000'		; right part of digit 1
	retlw	b'00100000'		; right part of digit 2
	retlw	b'00110000'		; right part of digit 3
	retlw	b'00010000'		; right part of digit 4
	retlw	b'00110000'		; right part of digit 5
	retlw	b'00110000'		; right part of digit 6
	retlw	b'00010000'		; right part of digit 7
	retlw	b'00110000'		; right part of digit 8
	retlw	b'00110000'		; right part of digit 9

codeTensL
	addwf	PCL, f
	retlw	b'00111100'		; left part of digit 0
	retlw	b'00010000'		; left part of digit 1
	retlw	b'00111010'		; left part of digit 2
	retlw	b'00011010'		; left part of digit 3
	retlw	b'00010110'		; left part of digit 4
	retlw	b'00001110'		; left part of digit 5
	retlw	b'00101110'		; left part of digit 6
	retlw	b'00011000'		; left part of digit 7 
	retlw	b'00111110'		; left part of digit 8
	retlw	b'00011110'		; left part of digit 9

codeHundredsR
	addwf	PCL, f
	retlw	b'00000111'		; right part of digit 0
	retlw	b'00000001'		; right part of digit 1
	retlw	b'00001011'		; right part of digit 2
	retlw	b'00001011'		; right part of digit 3
	retlw	b'00001101'		; right part of digit 4
	retlw	b'00001110'		; right part of digit 5
	retlw	b'00001110'		; right part of digit 6
	retlw	b'00000011'		; right part of digit 7
	retlw	b'00001111'		; right part of digit 8
	retlw	b'00001111'		; right part of digit 9

codeHundredsL
	addwf	PCL, f
	retlw	b'11000000'		; left part of digit 0
	retlw	b'01000000'		; left part of digit 1
	retlw	b'10000000'		; left part of digit 2
	retlw	b'11000000'		; left part of digit 3
	retlw	b'01000000'		; left part of digit 4
	retlw	b'11000000'		; left part of digit 5
	retlw	b'11000000'		; left part of digit 6
	retlw	b'01000000'		; left part of digit 7 
	retlw	b'11000000'		; left part of digit 8
	retlw	b'11000000'		; left part of digit 9

codeHundredsS
	addwf	PCL, f
	retlw	b'01000000'		; left part of digit 0
	retlw	b'00000000'		; left part of digit 1
	retlw	b'01000000'		; left part of digit 2
	retlw	b'00000000'		; left part of digit 3
	retlw	b'00000000'		; left part of digit 4
	retlw	b'00000000'		; left part of digit 5
	retlw	b'01000000'		; left part of digit 6
	retlw	b'00000000'		; left part of digit 7 
	retlw	b'01000000'		; left part of digit 8
	retlw	b'00000000'		; left part of digit 9

main
	clrf	PORTA
	clrf	PORTB
	clrf	PORTC
	clrf 	PORTD
	bsf	PORTD, 1		; turn off the heater
	movlw	b'00001001'		; enable ADC at AN2
	movwf	ADCON0			; and left-justify the result

  	bsf    	STATUS, RP0    		; change to BANK 1
	movlw	7
	movwf	CMCON0 ^ 0x80		; comparators off
	clrf	ANSEL ^ 0x80		; all inputs digital
	bsf	ANSEL ^ 0x80, 2		; excluding AN2:  analog input
	movlw	b'00010000'
	movwf	ADCON1 ^ 0x80		; ADC period = FOSC/8

	movlw	b'01100001'
	movwf	OSCCON ^ 0x80		; internal oscillator @4MHz

    	clrf	TRISA ^ 0x80		; STEP 1: set I/O ports
	bsf	TRISA ^ 0x80, 2		; RA2 is analog input
  	clrf    TRISB ^ 0x80           
	clrf	TRISC ^ 0x80
	clrf	TRISD ^ 0x80
	bcf	STATUS, RP0
  	bsf	STATUS, RP1		; switch to BANK 2

	bsf	LCDCON ^ 0x100, VLCDEN	; STEP 2: enable LCD bias voltage pins
	bcf	LCDCON ^ 0x100, CS1	; STEP 3: select clock source (FOSC/8092)	
	bcf	LCDCON ^ 0x100, CS0
	bcf	LCDCON ^ 0x100, LMUX1	; STEP 4: select multiplex mode (static)
	bcf	LCDCON ^ 0x100, LMUX0

	bcf	LCDPS ^ 0x100, 	WFT	; STEP 5: select waveform type (A)
	bcf	LCDPS ^ 0x100,	BIASMD	; STEP 6: select Bias mode
	bcf	LCDPS ^ 0x100, 	LP3	; STEP 7: select refresh rate
	bcf	LCDPS ^ 0x100, 	LP2	;    1:2	
	bcf	LCDPS ^ 0x100,	LP1
	bsf	LCDPS ^ 0x100,	LP0

	movlw	0xFF			; step 8: enable LCD segment lines
	movwf	LCDSE0 ^ 0x100
	movlw	b'10011111'
	movwf	LCDSE1 ^ 0x100
	movlw	b'11111110'
	movwf	LCDSE2 ^ 0x100
	clrf	LCDDATA0 ^ 0x100	; step 9: clear segment data registers
	clrf	LCDDATA1 ^ 0x100
	clrf	LCDDATA2 ^ 0x100

	bsf	LCDCON ^ 0x100,	LCDEN	; step 10: turn on the LCD driver module
	bcf	LCDCON ^ 0x100, SLPEN	; enable LCD in sleep mode

	bcf	STATUS, RP1		; back to BANK 0

	movlw	10
	movwf	coHigh			; start alarm at this value
	call 	greenLED

loop
	bcf	PORTD, 1		; 14 msec heating cycle
	movlw	14
	movwf	del
	call	delay
	bsf	PORTD, 1		; turn heater off

	movlw	250			; 981 msec waiting loop
	movwf	del
	call 	delay
	movlw	250
	movwf	del
	call 	delay
	movlw	250
	movwf	del
	call 	delay
	movlw	229
	movwf	del
	call 	delay

	bsf	PORTD, 3
	movlw	3			; start data reading impulse
	movwf	del
	call 	delay

	bsf	ADCON0, GO		; start AD conversion
	btfsc	ADCON0, GO		; conversion done?
 	 goto 	$-1			; not yet - wait (takes about 25usec)
	bcf	PORTD, 3		; end of data reading loop

	; PROCESSING THE SENSOR DATA 
	movf	ADRESH, w		; get the ADC value for processing
	btfss	STATUS, Z
	 addlw	-1			; correction to display 0 normally	
	movwf	coValue	
	call 	bin2bcd
	call 	displayCO

    	movf	coHigh, w
	subwf	coValue, w
	btfsc	STATUS, C
	 goto 	alarm
	call 	greenLED
	goto 	loop

alarm
	call 	buzz
	call 	redLED	
	goto 	loop

bin2bcd					; converts the value in Wreg into BCD
	clrf	hundreds
	clrf	tens
	clrf	units

dig2					; processing hundreds
	incf	hundreds, f
	addlw	-100
	btfsc	STATUS, C
	 goto	dig2
	decf	hundreds, f
	addlw	100

dig1					; processing tens
	incf	tens, f
	addlw	-10
	btfsc	STATUS, C
	 goto	dig1
	decf	tens, f
	addlw	10
	movwf	units			; processing units
	return

displayCO
	clrf	displayCodes		
	clrf	displayCodes+1
	clrf	displayCodes+2

	movf	hundreds, w		; displaying hundreds
	btfss	STATUS, Z		; display 100's if it non-zero
	 goto	disp100
	movf	tens, w			; now the 100's are 0, check the 10's
	btfss	STATUS, Z
	 goto	disp10			; display non-zero tens
	goto	disp1			; display just units if 10's are zero

disp100
	call 	codeHundredsS
	iorwf	displayCodes, f
	movf	hundreds, w
	call	codeHundredsR
	iorwf	displayCodes+1, f	
	movf	hundreds, w
	call	codeHundredsL
	iorwf	displayCodes+2, f

disp10
	movf	tens, w			; displaying tens
	call 	codeTensR
	iorwf	displayCodes, f
	movf	tens, w
	call	codeTensL
	iorwf	displayCodes+2, f

disp1
	movf	units, w		; displaying units
	call 	codeUnitsR
	iorwf	displayCodes, f
	movf	units, w
	call	codeUnitsL
	iorwf	displayCodes+1, f

	bsf	STATUS, RP1		; switch to BANK 2
	movf	displayCodes+2, w	; update LCD
	movwf	LCDDATA2 ^ 0x100
	movf	displayCodes+1, w
	movwf	LCDDATA1 ^ 0x100
	movf	displayCodes, w
	movwf	LCDDATA0 ^ 0x100
	bcf	STATUS, RP1		; back to BANK 0
	return

greenLED				; turn LED green
	bcf	PORTC, 1
	bsf	PORTD, 0
	return

redLED					; turn LED red
	bcf	PORTD, 0
	bsf	PORTC, 1
	return

buzz					; turn on buzzer for 100msec
	movlw	250			; buzzer freq 2400Hz (T=416usec)
	movwf	del
buzzL
	bsf	PORTB, 5
	movlw	69			; buzzer is on for 69*3=207usec
	movwf	buzzT
	decfsz	buzzT, f
	 goto	$-1
	bcf	PORTB, 5
	movlw	69			; buzzer is off for the second half-period
	movwf	buzzT
	decfsz	buzzT, f
	 goto	$-1
	decfsz	del, f
	 goto	buzzL	
	return

delay					; a delay for del milliseconds
	movlw 	200
	movwf	delTMP
delL	
	nop				; this loop takes 5us*200 = 1ms
	nop				; for PIC16F917 @ 4 Mhz
	decfsz	delTMP, f
	goto 	delL

	decfsz	del, f
	goto 	delay
	return
 END
