 TITLE  "tempAn.asm"       		; 0 - 50C termometer  
 List P=PIC16F684, R=DEC
 INCLUDE "p16f684.inc"

; data segment
 CBLOCK 0x20    
 temp, tempAve:2, cnt, tint		; storage for temp measurement               
 del, n, r, d, tmp  			; local variables 
 bcd32, bcd10				; BCD rep of 10*temp
 w_save, stat_save			; ISR variables 
 point, scale, button			; decimal point flag
 temp1, temp2, temp3		   	; temp readings for averaging
 segments:12	
 ENDC

; code segment
 PAGE
 __CONFIG _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

  ORG 0
	goto 	main

  ORG 4					; ISR
	movwf	w_save			; save W reg
	swapf	STATUS, w		; save stat reg without
	movwf	stat_save		; changing flags
	bcf	PIR1, 0			; clear TMR1 interrupt flag
		
	; main ISR body (get temp, round it off, and convert to BCD)
	decfsz	tint, f			; time to check temp?
	goto 	ISR_end			; not yet - back to main thread

	bsf	tint, 2			; update the temp interval cntr.
	call 	getTemp			; get temp reading from ADC 
	call	computeAve		; average temp readings 
	call 	bin2bcd			; convert it into BCD

ISR_end
	swapf	stat_save, w		; get orig flags in STATUS
	movwf	STATUS
	swapf	w_save, f		; restore the original W		
	swapf	w_save, w
	retfie

main       		               	; main code
	bcf	STATUS, RP0		; activate BANK 0
	clrf	PORTA			; initialize PORT A
	clrf	PORTC			; initialize PORT C
	movlw	0x07
	movwf	CMCON0			; comparators OFF

	; ADC configuration
	movlw	b'00001'
	movwf 	ADCON0 			; Left justify, Vdd Vref, AN0, On	
	bsf    	STATUS, RP0      	; change to BANK 1
	movlw	1
	movwf	ANSEL ^ 0x80		; make RA0/AN0 analog input
	movlw 	b'01110000' 		; set ADC Frc clock
	movwf 	ADCON1 ^ 0x80

	movlw	1
	movwf	PIE1 ^ 0x80		; enable TMR1 interrupt	

	movlw	b'0001001'
	movwf	TRISA ^ 0x80		; enable RA0 and RA3 for input
	clrf	TRISC ^ 0x80		; enable output on RC<5:0>
	bcf    	STATUS, RP0        	; back to BANK 0

	movlw	4			; INITIALIZATION
	movwf	tint			; setup temp reading interval (4sec)
	clrf	scale			; scale: 0=C  1=F
	call	initSegments		; initialize 7-seg codes
	call 	getTemp			; get the first temp reading
	movf	temp, w
	movwf	temp1
	movwf	temp2
	movwf	temp3
	movwf	n
	movlw	10
	movwf	bcd10			; setup scale (temp in C)
	call 	bin2bcd			; initialize temp BCD array

	; TMR1 configuration
	movlw	b'10110001'		; TMR1 with 1:8 prescaler
	movwf	T1CON
	movlw	b'11000000'		; enable periferial interrupts
	movwf	INTCON
	bcf	PIR1, 0			; clear timer interrupt flag

loop					; main loop
	btfss	PORTA, 3		; check C/F button status	
	goto	$+3
	clrf	button			; buton is not pressed - continue
	goto 	displayTemp		

	btfsc	button, 0		; button debouncing:
	goto	displayTemp		; button was pressed and not released

	bsf	button, 0		; button is just presed
	movf	scale, w		; toggle the C/F status
	sublw	1					
	movwf	scale			; scale = 1 - scale
	addlw	10			; W = 10 or 11 to display C/F
	movwf	bcd10			; setup the scale letter code
	call	bin2bcd			; according	to the scale (C/F)			

displayTemp
	movlw	6
	movwf	d			; by setting d=6 we start with
	swapf	bcd32, w		; the leftmost digit
	andlw	0x0F
	movwf	r			; status flags are not modified	
	btfss	STATUS, Z		; do not display the leading 0
	call	displayDigit

	decf	d, f			; get to the middle digit
	decf	d, f
	clrf	point
	movf	bcd32, w
	andlw	0x0F
	movwf	r			; we display it in any case
	movlw	0xF0
	andwf	bcd10, w
	btfsc	scale, 0		; Celcius?
	goto	$+3			; NO -proceed with displaying
	btfss	STATUS, Z		; turn on the decimal point?
	bsf	point, 7
	call	displayDigit

	clrf 	point
	decf	d, f			; get to the fraction digit
	decf	d, f
	swapf	bcd10, w		; get the fraction digit
	andlw	0x0F
	movwf	r	
	btfsc	scale, 0		; Celcius?
	goto 	$+2			; NO - proceed with displaying		
	btfss	STATUS, Z		; skip it if it is 0		
	call 	displayDigit

	decf	d, f
	decf	d, f
	movf	bcd10, w		; get the scale (C,F)
	andlw	0x0F
	movwf	r
	call	displayDigit
	
	goto loop

; procedures
getTemp					; get temp reading from ADC
	bsf	ADCON0, GO		; start ADC operation
	btfsc	ADCON0, GO		; and wait for its completion	
	goto 	$-1
	movf	ADRESH, w		; get higher 8 bits of ADC
	movwf	temp			; current temp reading 
	return

computeAve
	movf	temp, w			; get current temp reading
	movwf	tempAve			; compute 10-bit average
	clrf	tempAve+1		; of last 4 temp readings	
	movf	temp1, w
	addwf	tempAve, f		; add temp1 
	btfsc	STATUS, C
	incf	tempAve+1, f
	movf	temp2, w
	addwf	tempAve, f		; add temp2
	btfsc	STATUS, C
	incf	tempAve+1, f
	movf	temp3, w
	addwf	tempAve, f		; add temp3
	btfsc	STATUS, C
	incf	tempAve+1, f

	rrf	tempAve+1, f		; divide the sum in n+1:n by 4
	rrf	tempAve, f		; and round off the quitient
	rrf	tempAve+1, f		; pull bit9 of n into C flag
	rrf	tempAve, f		; add it to n
	btfsc	STATUS, C		; examine (old) bit 1 on n
	incf	tempAve, f		; rounding off

	movf	temp2, w		; slide temp readings 
	movwf	temp1
	movf	temp3, w
	movwf	temp2
	movf	temp, w
	movwf	temp3
	return

bin2bcd					; convert tempAve into BCD representation
	movf	tempAve, w
	btfsc	scale, 0
	call	convert			; convert tempAve into F scale
	movwf	n			; save it in n for processing

	call 	div10			; convert n into bcd32:bcd10.
	btfsc	scale, 0		; scale = F?
	goto	pack			; YES - skip Celcius processing
	rlf	n, f			; double the quotient
	rlf	r, f			; double the remainder
	movlw	10
	subwf	r, w
	btfss	STATUS, C
	goto	$+3
	movwf	r
	incf	n, f

	btfsc	r, 3			; add 1 to quotient if
	incf	n, f			; the remainder is 8 (rounding)
	movlw	0x05			; replace the decimal digit
	andwf	r, f			; with 0 or 5	
	btfss	STATUS, Z		; 0,2,8 -> 0;  4,6->5
	bsf	r, 0

pack
	movlw	0x0F			; clear left nibble in bcd10
	andwf	bcd10, f
	swapf	r, w			; swap nibbles in r	 
	addwf	bcd10, f		; decimal digit

	call 	div10			; pack the other two digits of n
	movf	r, w			; into bcd32
	movwf	bcd32
	call	div10
	swapf	r, w
	addwf	bcd32, f
	return

displayDigit   				; display digit r at position d
	movlw	b'111001'
	andwf	PORTA, f	
  	movf	d, w
	iorwf	PORTA, f		; start displaying the digit #d

	movf	r, w			; get parameter to w
	andlw	0x0F
	addlw	segments
	movwf	FSR         		; FSR is a pointer to the digit code
	movf	INDF, w			; w = segments[digit]
	addwf	point, w		; process the decimal point
	movwf	tmp
	rrf	tmp, f
	rrf	tmp, f
	movwf	PORTC       		; light on the digit
	movlw	b'11001111'		; processing the two higher bits
	andwf	PORTA, f		; of the 7-seg code
	movf	tmp, w
	andlw	b'00110000'	
	iorwf	PORTA, f
		
	movlw	4			; a 4ms delay
	movwf	del
	call 	delay				
	return

div10					; computing n = n/10 and r = n%10
	movlw	5
	movwf	cnt			; cnt = 5
	movf	n, w				
	movwf	r			; r = n
	clrf	n			; n = 0
	movlw	0xA0
	bsf	STATUS, C
		
	subwf	r, f			; loop that executes 5 times
	btfsc	STATUS, C
	goto	$+3
	addwf	r, f
	bcf	STATUS, C

	rlf	n, f			; C = bit of n/10
	movwf	tmp
	rrf	tmp, f
	movf	tmp, w			; shift right W

	decfsz 	cnt, f			; div is done in 5 loop iterations
	goto	$-10			; end of loop
    	return

initSegments				; set up 7-seg digit codes
  	movlw	b'00111111' 		; gidit 0 pgfedcba
	movwf	segments+0
	movlw	b'00000110' 		; digit 1
	movwf	segments+1
	movlw	b'01011011'		; digit 2
	movwf	segments+2
	movlw	b'01001111'		; digit 3
	movwf	segments+3
	movlw	b'01100110'		; digit 4	
	movwf	segments+4
	movlw	b'01101101'		; digit 5
	movwf	segments+5
	movlw	b'01111101'		; digit 6
	movwf	segments+6
	movlw	b'00000111'		; digit 7
	movwf	segments+7
	movlw	b'01111111' 		; digit 8
	movwf	segments+8
	movlw	b'01101111'		; digit 9
	movwf	segments+9
	movlw	b'00111001'		; letter C
	movwf	segments+10
	movlw	b'01110001'		; letter F
	movwf	segments+11
	return

delay					; a delay for del milliseconds
	movlw 	200
	
	sublw	1			; this loop takes 5us*200 = 1ms
	sublw	0			; for PIC16F684 @ 4 Mhz
	btfss	STATUS, Z
	goto 	$-3

	decfsz	del, f
	goto 	delay
	return

convert					; convert C into F
	bsf	STATUS, RP0		; switch to bank 1
	movwf	EEADR 			; set address (in Wreg) to read
	bsf	EECON1, RD		; load byte form EEPROM
	movf	EEDAT ^ 0x80, w		; move it to Wreg
	bcf	STATUS, RP0		; back to bank 0
	return

 ORG 0x2100				; EEPROM data
c2fTable
	dt 32, 32, 33, 33, 33, 34, 34, 35
	dt 35, 35, 36, 36, 36, 37, 37, 37
	dt 38, 38, 38, 39, 39, 40, 40, 40
	dt 41, 41, 41, 42, 42, 42, 43, 43
	dt 44, 44, 44, 45, 45, 45, 46, 46
	dt 46, 47, 47, 47, 48, 48, 49, 49
	dt 49, 50, 50, 50, 51, 51, 51, 52
	dt 52, 53, 53, 53, 54, 54, 54, 55
	dt 55, 55, 56, 56, 56, 57, 57, 58
	dt 58, 58, 59, 59, 59, 60, 60, 60
	dt 61, 61, 62, 62, 62, 63, 63, 63
	dt 64, 64, 64, 65, 65, 65, 66, 66
	dt 67, 67, 67, 68, 68, 68, 69, 69
	dt 69, 70, 70, 71, 71, 71, 72, 72
	dt 72, 73, 73, 73, 74, 74, 74, 75
	dt 75, 76, 76, 76, 77, 77, 77, 78
	dt 78, 78, 79, 79, 80, 80, 80, 81
	dt 81, 81, 82, 82, 82, 83, 83, 83
	dt 84, 84, 85, 85, 85, 86, 86, 86
	dt 87, 87, 87, 88, 88, 89, 89, 89
	dt 90, 90, 90, 91, 91, 91, 92, 92
	dt 92, 93, 93, 94, 94, 94, 95, 95
	dt 95, 96, 96, 96, 97, 97, 98, 98
	dt 98, 99, 99, 99, 100, 100, 100, 101
	dt 101, 101, 102, 102, 103, 103, 103, 104
	dt 104, 104, 105, 105, 105, 106, 106, 107
	dt 107, 107, 108, 108, 108, 109, 109, 109
	dt 110, 110, 110, 111, 111, 112, 112, 112
	dt 113, 113, 113, 114, 114, 114, 115, 115
	dt 116, 116, 116, 117, 117, 117, 118, 118
	dt 118, 119, 119, 119, 120, 120, 121, 121
	dt 121, 122, 122, 122, 123, 123, 123, 124

 end
