⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 thermostat.asm

📁 pic系列单片机得控制程序 主要进行温度采集和转换控制
💻 ASM
字号:
;
; thermostat.asm -- a simple automatic thermostat
;

;
; source code for gpasm by Byron Jeff - Oct 12th, 2001
; Cleaned for posting - Nov 22nd, 2001
;

; This controller drives set of relays based on the temperature. The
; temp is obtained via a Dallas Semiconductor 1620 digital thermometer.
; The Fahrenheit temp (converted from Celsuis read from the DS1620) is
; displayed on a 2 digit LED display. This software is set to drive the
; Heater from 68 to 73 degrees F, and the AC from 76 to 71 degrees F 

; LICENSE: This software can be used, modified, and redistributed to personal
; non-commercial use only. Please contact the author at byron@cc.gatech.edu
; for any commercial usage of this software.      
;
; define PIC device type
;
;	device	pic16F877

;
; define config fuses
;
;	__config	CP=off,WDT=off,PWRT=off,OSC=hs
	radix	dec	; Default numbers in decimal. I use 0x for hex.

;
; include PIC register definitions, and some macros
; 
	include "picreg.h"

; Define identifiers for strings
	cblock	0
	HELLO
	CRLF
	endc

; Bit numbers for the DS1620 thermometer. All on port C
	cblock	0
	DSDATA
	DSCLK
	DSRESET
	endc
	
; Define some variables - Note that some are leftovers from another project.
	cblock	0x2c
	tempw	
	dtempw	
	bcdhi
	bcdlo
	nine
	vportb
	vportd
	txout
	c1
	c2
	tempw1
	tempw2
	fahrtemp
	admapoffset
	admapcnt
	admapval
	adtarget
	pscnt
	psoff
	column
	dispcnt
	nexttmr1
	lastknob	; Has the last value of the knob.
	lasttemp	; Has the last value of the temp sensor.
	ftemphi		; Faharenheit temp
	ftemplo
	fshifthi	; Shift register for conversion
	fshiftlo	; Shift register for conversion
	display		; NOTE: This must be the last variable defined!
	endc	

;
; code start
;
	org	0
	goto	init

	org	4		; Interrupt vector
	bcf	INTCON,T0IF	; Clear the interrupt
	bsf	TMR1H,7
	retfie			; Leave.


getchar	andlw	15		; Only the low nybble
	addwf	PCL,F		; Jump table.

	dt	0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07	
	dt	0x7f,0x67,0x77,0x7c,0x39,0x5e,0x79,0x71	

highbcd	andlw	0x0f		; Mask off low part of nybble (already swapped)
	addwf	PCL,F		; Jump table
	dt	0x00,0x16,0x32,0x48,0x64,0x80,0x96

hex2bcd	movwf	tempw		; Save it in temp register
	swapf	tempw,W
	call	highbcd		; Get the BCD value of the high nybble
	movwf	bcdlo		; Save for later
	andlw	0xf0		; Mask off the low nybble for later add.
	movwf	bcdhi
	movlw	0x0f		; Mask off the high nybble of bcdlo
	andwf	bcdlo,F
	movlw	9		; Put a nine to subtract in the register
	movwf	nine

; Add the low nybbles together. Compensate if the sum is greater than 9
; Then add the result to the high nybble of the BCD of the high nybble.
; The low digit add plus the compensation may have pushed the low digit back
; into the hex range. Need to check and recompensate. For example 1F maps to
; (16 + 0F). The low digit sum is 15 hex and with the compensation is bumped
; to 1B. It needs to be compensated again by adding 6 more to get to 21 BCD.
	movf	tempw,W		; Get the original number
	andlw	0x0f		; Mask off high part of the nybble
	addwf	bcdlo,W		; Add the low nybble of the high digit value.
	subwf	nine,F		; See if bigger than nine
	btfss	STATUS,C	; Less than 9
	addlw	6		; Add 6 to compensate for hex digits from add.

; This is where recompensate the low digit sum if necessary. Note that 
; This one only checks the low digit of the sum since presumably there will
; be some high digit carry when adding 8+9+6 for example.
	movwf	bcdlo
	movlw	9		; Put a nine to subtract in the register
	movwf	nine
	movlw	0xf		; Mask for low nybble of low digit sum
	andwf	bcdlo,W
	subwf	nine,F		; See if bigger than nine
	clrw
	btfss	STATUS,C	; Less than 9
	movlw	6		; Add 6 to compensate for hex digits from add.
	addwf	bcdlo,W		; Add the low digit sum with any more comp...
	addwf	bcdhi,W		; Add the high part of BCD for final result.
	return

init
	bcf	STATUS,RP0			;register page 0
	movlw	0		; init PORTE0 to input
	movwf	PORTE		;initialize port E so serial is out and E0
	movlw	0x0
	movwf	PORTD		;initialize port D so that LEDs are on
	movlw	0x1a		; Make DSCLK high be default. Both LEDs on.
	movwf	PORTC		; Clear out PORTC.
	movlw	0
	movwf	PORTB		; All indicator LEDs and relays off.
	movlw	5
	movwf	T2CON
	clrf	column		; start display in column 0

	movlw	100		; Update display every 100 big loops
	movwf	dispcnt

	MOVLW   0x81             ; F/32 Clock, A/D is on, Channel 1 is selected
	MOVWF   ADCON0           ; 

	BCF     PIR1,   ADIF     ; Clear A/D interrupt flag bit 
	BCF     INTCON, PEIE     ; Disable peripheral interrupts FOR NOW!
	BCF     INTCON, GIE      ; disable all interrupts FOR NOW!
	clrf	lastknob
	clrf	lasttemp

	bsf	STATUS,RP0	;register page 1
	clrf    ADCON1           ; Configure A/D inputs 
	BCF     PIE1,ADIE     	; DISABLE A/D interrupts 

	movlw	0x80		; PORTB all outputs except for the switch.
	movwf	PORTB		; register TRISB
	movlw	0xe0		; For now the low 5 bits of PORTC is output
	movwf	PORTC		; Set it up.
	movlw	0x00		; Whole port as an output.
	movwf	PORTD		; register TRISD
	movwf	PORTE		; Port TRISE all outputs

	movlw	65
	movwf	T2CON		; PR2 set to 65 ticks for 52 uS delay
	bcf	STATUS,RP0	;register page 0
	clrf	PCLATH

	movlw	HELLO
	call	printstr

	movlw	10		; This is the mapping for temp range
	movwf	admapoffset	; Save in the offset


; Setup the DS1620 by setting the config byte
	bsf	PORTC,DSRESET	; Ready to send command
	movlw	0xac		; Get config command
	call	ds1620out	; Send it.
	call	ds1620in	; Get it
	bcf	PORTC,DSRESET	; Reset

; Now start the conversions
	bsf	PORTC,DSRESET	; Ready to send command
	movlw	0xee		; Get config command
	call	ds1620out	; Send it.

	bcf	PORTC,DSRESET	; Reset

temploop
	bsf	PORTC,DSRESET	; Ready to send command
	movlw	0xaa		; Get config command
	call	ds1620out	; Send it.
	call	ds1620in	; Get it
	bcf	PORTC,DSRESET	; Reset

; We note that the newly imported temp is also in tempw1. See if it's changed
	xorwf	lasttemp,W	; Are they the same
	btfsc	STATUS,Z	; They are not. so update
	goto	main		; Done for now

	movf	tempw1,W
	movwf	lasttemp	; This is now the new lasttemp

; Convert the value to Fahrenheit temp, the to decimal and print
	movf	tempw1,W
	call	ds1620toF
	movwf	fahrtemp	; Save the fahrenheit temp
	call	hex2bcd
	movwf	tempw1		; Save it
	call	getchar		; convert to LED char
	xorlw	255		; And complement for display
	movwf	display+1	; Stick in display
	swapf	tempw1,W	; Do the high nybble
	call	getchar
	xorlw	255		; And complement for display
	movwf	display		; Stick in display

; Now see if we need to alter the current state.
	btfsc	PORTB,0		; See if A/C is on
	goto	acstate		; It is. Go here.
	btfsc	PORTB,2		; See if heater is on
	goto	heatstate	; It is. Go here

; Everything is off. Test for extreme temps and turn on heater or AC to
; compensate if necessary.
	movlw	77		; Turn on AC at 77 degrees
	subwf	fahrtemp,W	; See if the temp matches
	btfsc	STATUS,C	; Continue if less than 77 degrees
	goto	ac_on		; Turn AC on.

	movlw	69		; Turn on heat at 68 degrees
	subwf	fahrtemp,W	; See if the temp matches
	btfsc	STATUS,C	; Done unless less than 69 degrees
	goto	main		; 

heat_on
	bsf	PORTB,2		; Turn on the indicator
	NOP
	bsf	PORTB,3		; And the relay
	goto	main

ac_on
	bsf	PORTB,0		; Turn on the indicator
	NOP
	bsf	PORTB,1		; And the relay
	goto	main

acstate				; AC is on. See if it needs to go off
	movlw	71		; Turn off AC at 71 degrees so it'll run longer
	xorwf	fahrtemp,W	; See if the temp matches
	btfss	STATUS,Z	; Done if no match
	goto	main		; 

	bcf	PORTB,0		; Turn off the indicator
	NOP
	bcf	PORTB,1		; And the relay
	goto	main

heatstate			; Heat is on. See if it needs to go off.
	movlw	73		; Turn off heat at 73F  so it'll run longer
	xorwf	fahrtemp,W	; See if the temp matches
	btfss	STATUS,Z	; Done if no match
	goto	main		; 

	bcf	PORTB,2		; Turn off the indicator
	NOP
	bcf	PORTB,3		; And the relay

main	
        BSF     ADCON0, GO       ; Start A/D Conversion 
adloop
	btfsc	ADCON0, GO	; Wait for the conversion to finish
	goto	adloop

	call	idelay		; Wait 52uS for A/D to settle.

	movf	ADRESH,W	; Check the result. Only the high 7 bits.
	call	admap		; Map to the range
	xorwf	lastknob,W	; See if the values are the same
	btfsc	STATUS,Z	; Continue if different
	goto	dodisplay

	movf	ADRESH,W	; Get the result. Only the high 7 bits.
	call	admap		; Map to the range
	movwf	lastknob	; and save it.	
	
	addlw	65		; 65 is the bottom temp
	call	hex2bcd		; Convert to decimal
	call	printbyte	; And print it
	movlw	CRLF		; And go to next line
	call	printstr

dodisplay
	decfsz	dispcnt,F	; See if it's time to update the display
	goto	temploop	; Nope. Top of main loop
	movlw	100		; Reset the display count
	movwf	dispcnt

	movlw	1		; Switch display
	xorwf	column,F
	movf	column,W	; Get the digit to display. Remember it's in
	addlw	display		; The high nybble.
	movwf	FSR
	movf	INDF,W
	bcf	PORTC,3		; Clear both displays
	NOP
	bcf	PORTC,4
	movwf	PORTD
	btfsc	column,W	; Turn on the current display
	goto	col1on
	bsf	PORTC,3
	goto	temploop
col1on	bsf	PORTC,4
	goto	temploop

dispbyte
	call	hex2bcd
	movwf	dtempw
	swapf	dtempw,W
	call	getchar
	movwf	INDF
	incf	FSR,F
	movf	dtempw,W
	call	getchar
	movwf	INDF
	incf	FSR,F
	return


; Output to the DS1620. Basically a serial out routine 
; above except that it's clocked.

ds1620out
	movwf	tempw1		; Save the char to send
	movlw	8		; Number of bits to send
	movwf	c2	
	bsf	STATUS,RP0	;register page 1
	bcf	PORTC,DSDATA	; Set data for output.
	bcf	STATUS,RP0	;register page 0

dsoutloop1	
	bcf	PORTC,DSCLK	; Make the clock low
	NOP			; Paranoia setting for ports.
	bcf	PORTC,DSDATA	; Output a 0. 
	btfsc	tempw1,0	; Test the next bit to send
	bsf	PORTC,DSDATA	; Output a 1 instead. Shorter code
	rrf	tempw1,F	; rotate the next bit in
	bsf	PORTC,DSCLK	; Now clock the bit in.
	decfsz	c2,F		; Output 8 bits
	goto	dsoutloop1		
	return			; All done.


	; Clock in 8 bits from the DS1620
ds1620in
	movlw	8		; Number of bits to receive
	movwf	c2	
	bsf	STATUS,RP0			;register page 1
	bsf	PORTC,DSDATA	; Set data for input.
	bcf	STATUS,RP0			;register page 0
	
dsinloop1	
	bcf	PORTC,DSCLK	; Make the clock low
	bcf	STATUS,C	; Clear for rotate
	btfsc	PORTC,DSDATA	; Transfer DSDATA to the carry flag
	bsf	STATUS,C	; Set carry if DSDATA is a 1
	rrf	tempw1,F	; rotate the next bit into the top of txout
	bsf	PORTC,DSCLK	; Setup clock for the next bit
	decfsz	c2,F		; Output 8 bits
	goto	dsinloop1		
	movf	tempw1,W	; Save the char received
	return			; All done.

	; Check for a particular config bit of the DS1620. The interesting bit
	; Is set in W before calling. We continouously read the config register
	; until the bit changes to 0.

ds1620check
	movwf	tempw2		; tempw2 is an unused temp register
dchkloop
	bsf	PORTC,DSRESET	; Ready to do command
	movlw	0xac		; Do a read command
	call	ds1620out	; Send it out
	call	ds1620in	; Read it in
	bcf	PORTC,DSRESET	; Done with command
	andwf	tempw2,W	; Mask only the interesting bit
	btfss	STATUS,Z	; Done if 0
	goto	dchkloop	; Loop until done
	return			; All done.

; Convert a ds1620 temp in W to Fahrenheit. The idea is to use a modified
; version of F=C*1.8+32. The DS1620 temp is in 0.5C increments. To include
; The 1/2 degree C we modify the formula to F=DS*0.9+32. To do the constant
; multiply by 0.9 we use a 16 bit register and the formla of x*0.9 = x - x/8 +
; x/64 + x/128. To get the last two terms, we divide by 256 (essentially trans
; ferring the upper byte of x to the lower byte) then shift left a couple of
; times. We can then shift left 3 more times from the x/64 term to get the x/8

ds1620toF			
	movwf	ftemphi		; Init registers
	movwf	fshiftlo	; Does the DS/256
	clrf	ftemplo
	clrf	fshifthi
	bcf	STATUS,C	

; We shift 1 bit left to make DS/128. Then we add it to the temp.
	rlf	fshiftlo,F
	rlf	fshifthi,F
	call	ds1620add

; Shift another bit to get DS/64. Add it again.
	rlf	fshiftlo,F
	rlf	fshifthi,F
	call	ds1620add

; Shift 3 bits up to get DS/8. Then negate and add. Don't worry about the 1
; on the end since only the 1st bit after the binary point is checked for 
; rounding.
	rlf	fshiftlo,F
	rlf	fshifthi,F
	rlf	fshiftlo,F
	rlf	fshifthi,F
	rlf	fshiftlo,F
	rlf	fshifthi,F
	comf	fshifthi,F
	comf	fshiftlo,F
	call	ds1620add

; Round up if the 1st bit after the binary point is 1
	btfsc	ftemplo,7
	incf	fshifthi,F

; Finally add the 32 to the whole part
	movlw	32
	addwf	ftemphi,F

; Put the result in W and return
	movf	ftemphi,W
	return

;	Routine to add the current shift value to the temp registers.
ds1620add
	movf	fshiftlo,W
	addwf	ftemplo,F
	btfsc	STATUS,C	; Check for overflow
	incf	ftemphi,F	; Add one to hi byte if overflow
 	movf	fshifthi,W
	addwf	ftemphi,F
	bcf	STATUS,C	; Make sure carry is clear upon leaving.
	return

	; Delay routines for Timer2
	; idelay resets timer 2 while delay uses current settings.
idelay	bcf	PIR1,TMR2IF	; Clear the timer 2 flag bit
	clrf	TMR2		; Clear timer2
delay	btfss	PIR1,TMR2IF	; Wait until timer2 flag comes up
	goto	delay
	bcf	PIR1,TMR2IF	; Clear it.
	return
	

admap	; Maps the A/D value into smaller range
	; Input: Value to map in W. offset in admapoffset
	; Output: mapping in W
	movwf	adtarget	; Save the target
	movf	admapoffset,W	; Get the offset
	clrf	admapcnt	; Zero the count
; This is a divide by subtraction. We subtract the offset until the target
; goes negative
admloop	subwf	adtarget,F	; Subtract the offset
	btfss	STATUS,C	; Positive result. More to do
	goto	admcont		; Finished if negative
	incf	admapcnt,F	; 
	goto	admloop		
admcont	movf	admapcnt,W
	return
	end




⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -