Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC 16F887 - Temperature Control - ADC - Some questions {Some code here}

Status
Not open for further replies.

Jugurtha

Member
Hello, everyone..

This is some code I wrote for the Temperature control. This is the first PIC program I actually write from scratch. Nigel Goodwin's tutorial 11 on Analog(ue) inputs (still can't get used to the UK spelling :) ) helped a bunch.

I have some questions:


- I noticed that in some programs, people don't bother with the OPTION register. How come ?

- I think I do not have to initialize the ADC (ADCON1, etc) everytime I want to do a conversion, am I right ? Is the code relating to the ADC correct, i.e : Do I only have to load the ADCON0 every conversion, or is that un-necessary too and I only have to set the GO bit and wait for it to complete ?

- I think the PIC in question can operate without an external oscillator: Is my __CONFIG correct ?

- The LM35 has a 10mV/°C characteristic. Say for Temp, there's a voltage of Voltage_Sensor..

Voltage_Sensor = 10/1000* Temp (Unit = volts)

I chose the Left justification, so I only have to read the ADRESH byte.


- This being the higher byte of the conversion result, I do have to multiply it by 256, right ?

Another point:

Let's say that to a certain temperature, the conversion gives 256. On 10 bits, 256 gives:

A9 A8 A7 A6 A5 A4 A3 A2 A1 A0
0 1 0 0 0 0 0 0 0 0


A1 and A0 are dismissed.

Or, the result being Left justified, I get :

A9 A8 A7 A6 A5 A4 A3 A2 A1 A0
0 0 0 0 0 0 0 1 0 0

Then multiply 00000001 by 256 (shift 8 times) to get the result 256 = 1 * 256.

I think this makes more sens, since it's a high byte.

I know this might be obvious, but I'd rather not have assumptions.

Thank you.



Code:
;*******************************************************************************************
	;*		             PCB Etching Tank - Temperature Control 	v 0.D  		               *
	;*-----------------------------------------------------------------------------------------*
	;*    		     Jugurtha Hadjar - Raouf Moualdi - Iheb Renai                              * 
	;*-----------------------------------------------------------------------------------------*
	;*          Pr. Youcef Smara                       Dr. Samir Boukhenous                    *
	;*-----------------------------------------------------------------------------------------*
	;*          Université des Sciences et de la Technologie Houari Boumédiène                 *
	;*-----------------------------------------------------------------------------------------*



	; PIC used: PIC 16F887 40 PIN, Dual in line. 10 bit ADC. Only 8 bits will be used.
	; Sensor  : LM 35 (as of this version) . 10mv/°C slope.



	; TODO: Project Due for June, 26th 2012. Give to Pr Smara.
	; TODO: Figure out OPTION_REG.
	; TODO: Delete the goto START line if there's nothing between
	;		that line and the START label.		
	; TODO: Delete the goto Initialize_PIC if there's nothing below it.





	; We'll use some subroutines for temperature checks, etc...

	;***********************************************************************************
	;*                                   THE SETUP                                     *
	;***********************************************************************************

		LIST p=16F887, r=dec, f=inhx8m

	#include "p16F887.inc"	                                         
		                                    												  

	 
		__config _CPD_OFF  & _LFOSC & _MCLRE_ON  & _CP_OFF & _WDT_OFF & _INTOSC

	   
															  ;Watchdog Timer  OFF
													  		  ;Power Up Timer  ON
		    												 
	#define Relay_Pin  PORTB,1
	#define Sensor_Pin PORTA,0
	

		cblock 0x20      ; General Purpose Registers start here in a PIC16F887.
	
			Delay_Counter_1, Delay_Counter_2, Temp_Sensor, Voltage_Sensor_ADC


		endc
	



	 
	;******************************************************************************************
	;*                             OPTION_REG Description                                     * 
	;*----------------------------------------------------------------------------------------*
	;* Bit7:RBPU(active low) PORTB Pull-Up Enable Bit (1, Disabled)                           *
	;* Bit6:INTEDG, Interrupt Edge Select Bit(0, Interrupt on falling edge B0/INT)            *
	;* Bit5:T0CS, TMR0 Clock Source Select Bit (0, Internal Instruction Cycle Clock)          *
	;* Bit4:T0SE, TMR0 Source Edge Select Bit(1, Increment on Hi-to-Lo transition on RA4)     *
	;* Bit3:PSA,PRESCALER Assignment Bit(0, Prescaler assigned to TMR0 module)                *
	;* Bit2-0:PRESCALER Select Bits (110, 1:128)                                              *
	;******************************************************************************************


		

	
		org 0x00              ;Reset Vector.
	
		   
	 	goto Initialize_PIC

		  	

	Initialize_PIC
	  	
		clrf PORTA
		clrf PORTB	; This includes the Relay_Pin (PORTB, 1 - Heater) which is 
					; cleared (turned off) by default for security measures.
		clrf PORTC


		bcf STATUS, RP0

		movlw 0x96               
		banksel OPTION_REG
		movwf OPTION_REG      ; Configure OPTION_REG (81h in Bank1).



		   
		
		  goto START		; Here we go..


	START	 


	Initialize_ADC
	
		banksel ADCON1
		movlw   b'0000000'	; Left justified. The ADC has a resolution of 10 bits.
							; We'll read the higher 8 bits in ADRESH, and sacrifice
							; the remaining 2 bits in the higher end of ADRESL.
						    ; VDD & VSS for VCFG1 & VCFG0

		movwf   ADCON1
		banksel TRISA
		bsf	    TRISA, 0    ; Set RA0 (Pin2) to Input 
		banksel ANSEL
		bsf	    ANSEL, 0    ; Set RA0 as Analog .. 
							; Now RA0 is Analog, and is Input.
							; RA0 is ready to receive information 
							; from the sensor.

	
		banksel ADCON0
		movlw   b'11000001'     ; The ADCON0 Register:
							    ; ADCS1 ADCS0 CHS2 CHS1 CHS0 GO_DONE - ADON

							    ; ADCS1 ADCS0    : 11 Conversion Clock frequency Frc
							    ;                  4 us typical.
							    ; CHS2 CHS1 CHS0 : 000   RA0
							    ; GO_DONE        : 0 . Setting it to 1 starts conversion.
								;                  automatically cleared when conversion is done.
							    ; ADON           : 1 . The ADC is enabled.
	

		movwf   ADCON0
	

	ADC_Get
	
		movlw   d'100'          ; A value which will be decreased to lose some time.
		movwf   Delay_Counter_2

		call	Delay		; Acquisition Time : See Datasheet "ADC time requirements".
							; Might as well be the best example of Cargo Cult Programming from me.
	

		bsf	    ADCON0, GO	; Start Analog to Digital Conversion.
		btfsc   ADCON0, GO	; Check if the conversion is finished.
		goto    $ - 1
	
		banksel ADRESH
		movf    ADRESH, w             ; Get the ADRESH byte and put it in Voltage_Sensor_ADC
		movwf   Voltage_Sensor_ADC	  ; The value is the digitized analog voltage from the sensor.
									  ; In order to compare it with a temperature, we need to 
									  ; calculate to which temperature in Celsius a certain		
									  ; voltage from the LM35 corresponds. (10mv/°C).
						
									  ; Need to figure out an "easy" way to do multiplication.

	
	
	


	;Check_Low_Threshold

		movlw	d'40'			  ; That's our Low Threshold
		call	Check_Temperature ; In order to know if the temperature from
								  ; the sensor is lesser or greater than our
								  ; threshold, we need to perform a substraction
								  ; the Carry flag in the STATUS register tells us
								  ; something about the sign, thus is it greater or lesser?
	
		btfsc	STATUS, C		  ; Is the result of the substraction in Check_Temperature positive?
								  ; If yes, it means that the temperature from the sensor
		call	Temp_Is_Low		  ; is less than our Low Threshold, hence we need to
								  ; turn the Relay_Pin on. We call the Temp_Is_Low subroutine for that.
								  
	
	






	;------------------------------------------------------------------------
	;Check_High_Threshold
	;------------------------------------------------------------------------

		movlw	d'42'			; That's our High Threshold
		call	Check_Temperature

		btfss	STATUS, C		; Is the Temperature from the Sensor
								; greater than the High threshold (42°C) ?

		call	Temp_Is_High	; If yes, i.e the temperature is HIGH, then
								; it's time to turn off the heater.
								; If not, i.e the Temperature from the sensor
								; is less than the High threshold and it's more
								; than the Low Threshold, then do nothing.
	

	Hysteresis

		goto	ADC_Get			; Here land all cases that need to keep the state of the heater.
								; Basically, all cases have been checked (> High Threshold ? < Low Threshold ? Between the two?)
								; And the only thing to do now, is to perform another conversion and
								; start a new cycle.





	;      goto START			TODO: Remove that line at the end if it's not necessary.





	;----------------------------------------------------------------------------------
	;									SUBROUTINES
	;----------------------------------------------------------------------------------


;	-----------------------------------------------
	Check_Temperature

		subwf   Temp_Sensor, w
	
		return
;	-----------------------------------------------



;	-----------------------------------------------
	Temp_Is_Low

		btfss	Relay_Pin	; Is the relay ON ? If it is, then return. (let it that way)
		bsf		Relay_Pin	; If it's OFF, then turn it ON and return.
						
		return
;	-----------------------------------------------


;	-----------------------------------------------
	Temp_Is_High

		btfsc	Relay_Pin	; Is the Relay OFF? If it is, then return. (let it off).
		bcf		Relay_Pin	; If it's ON, then turn it OFF and return.

		return
;	-----------------------------------------------


;	-----------------------------------------------
	Delay

		decfsz  Delay_Counter_2, f	; Teenage wasteland. Lose some time.
		goto    Delay				; Emo stuff...

		return
;	-----------------------------------------------



		end
 
Last edited:
hi Jugurtha,

When using the 8 bit mode for the ADC, the ADRESH contains the 'count value' of the conversion, giving a count value of 000 thru 255.

The LM35 giving a 10mV/Cdeg increase, say over a temperature range of 0C thru 100C would have a LM35 output of 1V maximum.

Using the PIC's internal Vref of 5V, the max count would be [1/5] *255 = 51. !!

If you used an external Vref of +2.5V, the max count would be 102.!! out of a possible max of 255.

The output of the LM35 can be amplified in order to give a maximum count value at the required maximum expected temperature .

E.
 
Hey Eric,

I didn't understand the full meaning. Let me see if I got you right .. Say the temperature is 100°C. So the corresponding counting is 255, does this mean that the ADRESH will be 11111111 ?

If so, I only have to convert that counting to the actual corresponding temperature to be used to compare against thresholds.
 
hi
no, the count will only be (1/5)*255
 
Since the LM35 outputs 10mV per 'C and is only useful to about 120'C, probably less in normal use. So it's max output voltage is say 1.2v which means you could use the ADC set to "right justified" and just use the ADRESL byte, which gives full resolution of the ADC and will still work ok up to 1.25v.

So if that was done each ADC count is roughly 0.5'C.

If your Vdd was 5.12v than each ADC count would be exactly 0.5'C.
 
That's what I wanted to do in the first place, but somehow got confused in all that stuff ( The temperatures I am interested in won't be as high as to set the 9th or 10th bit, hence me taking only one byte, the higher one as not to lose the two most signifcant bits that would ... never be high .. Kind of silly when I think about it.


Now, after some thinking .. I have changed my approach. It will no longer be comparing two "temperatures" .. But comparing two counts: The count from the converted voltage against the EXPECTED count calculated on paper for each of the thresholds would trigger. This way, I won't do any multiplications and divisions and all.

So for example:

For 10 bits and 5 V :

A temperature of 42 °C would have as a count 85.932 (86 to be safe)
A temperature of 40 °C would have as a count 81.84 (81 to be nice)


So I only have to check each count against these two values to know that I have reached the temperature, instead of doing tedious calculations :)


(Had I only used 8 bits, the values would be 20 and 21 which are way too close. For two temperatures 2°C apart, their counts are only 1 apart. That's stupid.)

Here's the code, somehow modified:

Code:
	;*******************************************************************************************
	;*		             PCB Etching Tank - Temperature Control 	v 0.E  		               *
	;*-----------------------------------------------------------------------------------------*
	;*    		     Jugurtha Hadjar - Raouf Moualdi - Iheb Renai                              * 
	;*-----------------------------------------------------------------------------------------*
	;*          Pr. Youcef Smara                       Dr. Samir Boukhenous                    *
	;*-----------------------------------------------------------------------------------------*
	;*          Université des Sciences et de la Technologie Houari Boumédiène                 *
	;*-----------------------------------------------------------------------------------------*



	; PIC used: PIC 16F887 40 PIN, Dual in line. 10 bit ADC. Only 8 bits will be used.
	; Sensor  : LM 35 (as of this version) . 10mv/°C slope.



	; TODO: Project Due for June, 26th 2012. Give to Pr Smara.
	; TODO: Figure out OPTION_REG.
	; TODO: Delete the goto START line if there's nothing between
	;		that line and the START label.		
	; TODO: Delete the goto Initialize_PIC if there's nothing below it.


	; UPDATE 0.D --> 0.E	:	Changing from Left justified, to right justified.
	;							We'll be using the lower 8 bits (ADRESL).

	; If the count is 255, then the temperature is 500°C and voltage = 5V.
	; so 8 bits are enough.						


	; We'll use some subroutines for temperature checks, etc...

	;***********************************************************************************
	;*                                   THE SETUP                                     *
	;***********************************************************************************

		LIST p=16F887, r=dec, f=inhx8m

	#include "p16F887.inc"	                                         
		                                    												  

	 
		__config _CPD_OFF  & _LFOSC & _MCLRE_ON  & _CP_OFF & _WDT_OFF & _INTOSC

	   
															  ;Watchdog Timer  OFF
													  		  ;Power Up Timer  ON
		    												 
	#define Relay_Pin  PORTB,1
	#define Sensor_Pin PORTA,0
	

		cblock 0x20      ; General Purpose Registers start here in a PIC16F887.
	
			Delay_Counter_1, Delay_Counter_2, Temp_Sensor, Voltage_Sensor_ADC


		endc
	



	 
	;******************************************************************************************
	;*                             OPTION_REG Description                                     * 
	;*----------------------------------------------------------------------------------------*
	;* Bit7:RBPU(active low) PORTB Pull-Up Enable Bit (1, Disabled)                           *
	;* Bit6:INTEDG, Interrupt Edge Select Bit(0, Interrupt on falling edge B0/INT)            *
	;* Bit5:T0CS, TMR0 Clock Source Select Bit (0, Internal Instruction Cycle Clock)          *
	;* Bit4:T0SE, TMR0 Source Edge Select Bit(1, Increment on Hi-to-Lo transition on RA4)     *
	;* Bit3:PSA,PRESCALER Assignment Bit(0, Prescaler assigned to TMR0 module)                *
	;* Bit2-0:PRESCALER Select Bits (110, 1:128)                                              *
	;******************************************************************************************


		

	
		org 0x00              ;Reset Vector.
	
		   
	 	goto Initialize_PIC

		  	

Initialize_PIC
	  	
		clrf PORTA
		clrf PORTB	; This includes the Relay_Pin (PORTB, 1 - Heater) which is 
					; cleared (turned off) by default for security measures.
		clrf PORTC


		bcf STATUS, RP0

		movlw 0x96               
		banksel OPTION_REG
		movwf OPTION_REG      ; Configure OPTION_REG (81h in Bank1).



		   
		
		  goto START		; Here we go..


START	 


Initialize_ADC
	
		banksel ADCON1
		movlw   b'1000000'	; Right justified. The ADC has a resolution of 10 bits.
							; We'll read the higher 8 bits in ADRESL since the temperatures
							; we are interested in won't make it to the 9th or 10th bit.
						    ; VDD & VSS for VCFG1 & VCFG0

		movwf   ADCON1
		banksel TRISA
		bsf	    TRISA, 0    ; Set RA0 (Pin2) to Input 
		banksel ANSEL
		bsf	    ANSEL, 0    ; Set RA0 as Analog .. 
							; Now RA0 is Analog, and is Input.
							; RA0 is ready to receive information 
							; from the sensor.

	
		banksel ADCON0
		movlw   b'11000001'     ; The ADCON0 Register:
							    ; ADCS1 ADCS0 CHS2 CHS1 CHS0 GO_DONE - ADON

							    ; ADCS1 ADCS0    : 11 Conversion Clock frequency Frc
							    ;                  4 us typical.
							    ; CHS2 CHS1 CHS0 : 000   RA0
							    ; GO_DONE        : 0 . Setting it to 1 starts conversion.
								;                  automatically cleared when conversion is done.
							    ; ADON           : 1 . The ADC is enabled.
	

		movwf   ADCON0
	

ADC_Get
	
		movlw   d'100'          ; A value which will be decreased to lose some time.
		movwf   Delay_Counter_2

		call	Delay		; Acquisition Time : See Datasheet "ADC time requirements".
							; Might as well be the best example of Cargo Cult Programming from me.
	

		bsf	    ADCON0, GO	; Start Analog to Digital Conversion.
		btfsc   ADCON0, GO	; Check if the conversion is finished.
		goto    $ - 1
	
		banksel ADRESH
		movf    ADRESH, w             ; Get the ADRESH byte and put it in Voltage_Sensor_ADC
		movwf   Voltage_Sensor_ADC	  ; The value is the digitized analog voltage from the sensor.
									  ; In order to compare it with a temperature, we need to 
									  ; calculate to which temperature in Celsius a certain		
									  ; voltage from the LM35 corresponds. (10mv/°C).
						
									  ; Need to figure out an "easy" way to do multiplication.

	
	;------------------------------------------------------------------------
	;							   Check_High_Threshold
	;------------------------------------------------------------------------

		movlw	d'86'				; That's our High Threshold.
		call	Check_Temperature	; Go to the end of the code
									; in "A Little Story About Conversion" to see
									; where did this value come from.
									; In a nutshell, that's the expected	
									; result a temperature of 42 °C would give 
									; after being converted by a 10 bits , 5 V Vref. ADC.





		btfss	STATUS, C		; Is the Temperature from the Sensor
								; greater than the High threshold (42°C) ?

		call	Temp_Is_High	; If yes, i.e the temperature is HIGH, then
								; it's time to turn off the heater.
								; If not, i.e the Temperature from the sensor
								; is less than the High threshold and it's more
								; than the Low Threshold, then do nothing.
	


	
	;----------------------------------------------------------------------------
	;								Check_Low_Threshold
	;----------------------------------------------------------------------------

		movlw	d'81'			  ; That's our Low Threshold

		call	Check_Temperature ; In order to know if the temperature from
								  ; the sensor is lesser or greater than our
								  ; threshold, we need to perform a substraction
								  ; the Carry flag in the STATUS register tells us
								  ; something about the sign, thus is it greater or lesser?
	
		btfsc	STATUS, C		  ; Is the result of the substraction in Check_Temperature positive?
								  ; If yes, it means that the temperature from the sensor
		call	Temp_Is_Low		  ; is less than our Low Threshold, hence we need to
								  ; turn the Relay_Pin on. We call the Temp_Is_Low subroutine for that.
	  


Hysteresis

		goto	ADC_Get			; Here land all cases that need to keep the state of the heater.
								; Basically, all cases have been checked (> High Threshold ? < Low Threshold ? Between the two?)
								; And the only thing to do now, is to perform another conversion and
								; start a new cycle.


	;      goto START			TODO: Remove that line at the end if it's not necessary.





	;----------------------------------------------------------------------------------
	;									SUBROUTINES
	;----------------------------------------------------------------------------------



;----------------------------------------------------------

Check_Temperature

		subwf   Voltage_Sensor_ADC, w
	
		return
;----------------------------------------------------------


Temp_Is_Low

		btfss	Relay_Pin	; Is the relay ON ? If it is, then return. (let it that way)
		bsf		Relay_Pin	; If it's OFF, then turn it ON and return.
						
		return
;-----------------------------------------------------------


Temp_Is_High

		btfsc	Relay_Pin	; Is the Relay OFF? If it is, then return. (let it off).
		bcf		Relay_Pin	; If it's ON, then turn it OFF and return.

		return
;-----------------------------------------------------------


Delay

		decfsz  Delay_Counter_2, f	; Teenage wasteland. Lose some time.
		goto    Delay				; Emo stuff...

		return
;-----------------------------------------------------------


;	A Little Story About Conversion


; 	Let's talk about the conversion a bit:
;	Let's say we use Vref = 5V of the supply.
;	The output voltage of the LM35 sensor is
;	Voltage_Sensor = Temperature * 10 / 1000 V
;	Voltage_Sensor = Temperature / 100

;	The result of the conversion of Voltage_Sensor
;	is called Voltage_Sensor_ADC.

;	5 volts input correspond to 255 (8 bits to 1)
;	Voltage_Sensor corresponds to Voltage_Sensor_ADC (or Count)

;	Voltage_Sensor_ADC = Voltage_Sensor * 255 / 5
	Voltage_Sensor_ADC = Voltage_Sensor * 51

;	Knowing that Voltage_Sensor = Temperature / 100

;	We get:

;	Voltage_Sensor_ADC = (Temperature / 100) * 51

;	Voltage_Sensor_ADC = Temperature * (51/100)	


;	This means that if the Temperature is 100 °C
;	The result of the conversion will be:
;	100 * 51 / 100 = 51.

;	This relation will help translate the "Count" or
;	Voltage_Sensor_ADC to the corresponding temperature.

;	Temperature = Voltage_Sensor_ADC * (100/51)

;	Which means that when we read ADRESL
;	Let's say it has a value or 30.
;	The Temperature which caused this number is
;	Temperature = 30 * (100/51)
;	Which is roughly 58 °C.

; 	Now I get the idea that I can calculate the Count for
;	my two thresholds, and instead of having the PIC compute
;	the corresponding temperature for each acquisition..
;	I'll have it compare each ADRESL with the count corresponding
;	to each threshold.

;	Example: Low_Threshold = 40 °C.
;	Voltage_Sensor_ADC_Low  = 40 * 51 / 100 = 20.4 (we'll worry floating point later).
;	Voltage_Sensor_ADC_High = 42 * 51 / 100 = 21.42.

;	So I'll just match ADRESL with 20 and 21.

;	However ... 20 and 21 are kind of too close.
;	We have two values 2 °C apart (that's huge)
;	Yet, the corresponding counts are very close.
;	That's not good
;	I understand why Ericgibbs and Mr RB insisted on using the whole 10 bits.


;	Maybe use the whole 10 bits like he suggested.

;   5V ---------------> 1023
;	Voltage_Sensor --->	 Voltage_Sensor_ADC

;	Voltage_Sensor_ADC = Voltage_Sensor * 1023 / 5
;	Voltage_Sensor_ADC = (Temperature / 100) * 1023 : 5

;	Voltage_Sensor_ADC = Temperature * 1023 / 500

;	For a temperature of 40 °C:
;	Voltage_Sensor_ADC_Low  = 40 * 1023 / 500 = 81.84
;	Voltage_Sensor_ADC_High = 42 * 1023 / 500 = 85.932

;	They are more spaced, so it's better.
;	I'll use the whole 10 bits.
;	Then match ADRESL with 86 and 81 for the thresholds.

		end
 
Last edited:
Depending on the temp range (<60 C? ) expected for your app u could employ the built in 0.6V Ref. It seems to have a worst case +/- 8% accuracy dependent on your device temp & supply voltage stability.
Have a detailed read here as the spec sheet has limited details.

With this Vref your 10 bit resolution is much better than 0.1 C, @ 8 bits resolution is about 0.25C
https://www.microchip.com/forums/tm.aspx?m=286058&mpage=1&print=true
 
Hi,

I got it to work somehow, but I'm not exactly looking at he output I was expecting. I corrected so many stuff going over the datasheets (internal oscillator, OSCCON, ADCON0, etc...)

The project deadline is behind me, the year is over and I passed to the 5th year. Still, I can't let go before it works perfectly.

Before I get to the meat, a little question:

When comparing two values (which one is greater), is it better to do a

Code:
subwf Register_Containing_Value, w


And then, if there's no carry it's negative (because it's a two's complement), and if there is carry, it's positive (Sounds counter intuitive, but you can do the maths 0100 - 0101 = 0100 + 1011 = 1111 = -1 without carry.

0101 - 0100 = 0101 + 1100 = [1] 0001 (With carry)

OR: Is it better to rotate through carry and check the carry ? Like 1111 rotated through carry gives [1] 1110 and the carry flag is set.

0001 rotated through carry gives [0] 0010 and the carry flag is clear. Which can be more intuitive.


Now my question:

I'm not getting the expected values. I put a DC voltage source on RA0 (where the Sensor must go) to emulate the sensor voltage.

As a reminder, it is an LM35 with a 10mv/°C slope. So unless Monica Bellucci touches the sensor, the temperature shouldn't be as high as to change ADRESH.

But I'm getting the opposite. For the voltage 0.45 volts (45°C), ADRESL = 0 and ADRESH = 23. And the Relay is activated although it shouldn't (max temperature should be 42 and everything beyond should turn the relay off).

For voltage = 0.43 volts (43°C), ADRESL = 128 and ADRESH = 25. ?



If you have Proteus, I'll have the MPLAB files and the Proteus files.

Here's the code (most of it are comments, it's just 70 words or so).


Thank you so much for your time.

Code:
	;*******************************************************************************************
	;*		             PCB Etching Tank - Temperature Control 	v 1.0  		               *
	;*-----------------------------------------------------------------------------------------*
	;*    		     Jugurtha Hadjar - Raouf Moualdi - Iheb Renai                              * 
	;*-----------------------------------------------------------------------------------------*
	;*          Pr. Youcef Smara                       Dr. Samir Boukhenous                    *
	;*-----------------------------------------------------------------------------------------*
	;*          Université des Sciences et de la Technologie Houari Boumédiène                 *
	;*-----------------------------------------------------------------------------------------*



	; PIC used: PIC 16F887 40 PIN, Dual in line. 10 bit ADC. Only 8 bits will be used.
	; Sensor  : LM 35 (as of this version) . 10mv/°C slope.



	; TODO: Project Due for June, 26th 2012. Give to Pr Smara.
	; TODO: Figure out OPTION_REG.
	; TODO: Delete the goto START line if there's nothing between
	;		that line and the START label.		
	; TODO: Delete the goto Initialize_PIC if there's nothing below it.


	; UPDATE 0.D --> 0.E	:	Changing from Left justified, to right justified.
	;							We'll be using the lower 8 bits (ADRESL).
	
	; UPDATE 0.F			:   Changed a line of code where I left ADRESH instead of ADRESL
								;after update 0.E.


	; If the count is 255, then the temperature is 500°C and voltage = 5V.
	; so 8 bits are enough.						


	; We'll use some subroutines for temperature checks, etc...

	;***********************************************************************************
	;*                                   THE SETUP                                     *
	;***********************************************************************************

		LIST p=16F887, r=dec, f=inhx8m

	#include "p16F887.inc"	                                         
		                                    												  

	 
	__config _config1,  _CPD_OFF & _MCLRE_OFF  & _CP_OFF & _WDT_OFF & _INTOSCIO

	; INTOSCIO: We don't need any clock for external devices, so we 
	; OSC1 and OSC2 are available for general purpose I/0. 
	; See page 67 of the datasheet. (INTOSC and INTOSCIO Modes).

 
	__config _config2, _WRT_OFF

	;_INTOSC &

	   
															  ;Watchdog Timer  OFF
													  		  ;Power Up Timer  ON
		    												  
	#define Relay_Pin  PORTB,1
	#define Sensor_Pin PORTA,0
	

		cblock 0x20      ; General Purpose Registers start here in a PIC16F887.
	
			Delay_Counter_1, Temporary_Var, Voltage_Sensor_ADC


		endc
	



	 
	;******************************************************************************************
	;*                             OPTION_REG Description                                     * 
	;*----------------------------------------------------------------------------------------*
	;* Bit7:RBPU(active low) PORTB Pull-Up Enable Bit (1, Disabled)                           *
	;* Bit6:INTEDG, Interrupt Edge Select Bit(0, Interrupt on falling edge B0/INT)            *
	;* Bit5:T0CS, TMR0 Clock Source Select Bit (0, Internal Instruction Cycle Clock)          *
	;* Bit4:T0SE, TMR0 Source Edge Select Bit(1, Increment on Hi-to-Lo transition on RA4)     *
	;* Bit3:PSA,PRESCALER Assignment Bit(0, Prescaler assigned to TMR0 module)                *
	;* Bit2-0:PRESCALER Select Bits (110, 1:128)                                              *
	;******************************************************************************************


		

	
		org 0x00              ;Reset Vector.
	
		   
	 	goto Initialize_PIC

		  	

Initialize_PIC
	  	
banksel TRISA
		bsf	    TRISA, 0    ; Set RA0 (Pin2) to Input 
		banksel ANSEL
		bsf	    ANSEL, 0    ; Set RA0 as Analog .. 
							; Now RA0 is Analog, and is Input.
							; RA0 is ready to receive information 
							; from the sensor.

		banksel TRISB
		clrf	TRISB	; Set RB1 to Output.		


		banksel PORTA

		clrf PORTA
		clrf PORTB	; This includes the Relay_Pin (PORTB, 1 - Heater) which is 
					; cleared (turned off) by default for security measures.
		clrf PORTC

				
		   
		banksel OSCCON
		
		movlw B'01100001'
		movwf OSCCON

; OSCCON REGISTER BITS
		
		; - IRCF2 IRCF1 IRCF0 OSTS HTS LTS SCS
		; 0 1     1     0     0    ?   ?   1

		; IRCF<2:1> 110 4 MHz (LFINTOSC).
		; OSTS      0   Device is running from Internal Oscillator
		;				(in our case, HFINTOSC).
		; HTS		Is HFINTOSC stable ? (Read)
		; LTS		Is LFINTOSC stable ? (Read)
		; SCS 		1	Internal Oscillator is used for system clock.
		
		

		; - : Unimplemented, read as 0.
		




		  goto START		; Here we go..


START	 


Initialize_ADC
	
		banksel ADCON1
		movlw   b'1000000'	; Right justified. The ADC has a resolution of 10 bits.
							; We'll read the higher 8 bits in ADRESL since the temperatures
							; we are interested in won't make it to the 9th or 10th bit.
						    ; VDD & VSS for VCFG1 & VCFG0

		movwf   ADCON1
		

		banksel ADCON0
		movlw   b'10000001'     ; The ADCON0 Register:
							    ; ADCS1 ADCS0 CHS2 CHS1 CHS0 GO_DONE - ADON

							    ; ADCS1 ADCS0    : 10 Conversion Clock frequency FOSC/32
							    ;                  To respect TAD time. See PAGE 103.
								;					Shaded cases are eliminated.

							    ; CHS2 CHS1 CHS0 : 000   RA0
							    ; GO_DONE        : 0 . Setting it to 1 starts conversion.
								;                  automatically cleared when conversion is done.
							    ; ADON           : 1 . The ADC is enabled.
	

		movwf   ADCON0
	

ADC_Get
	
		

		call	Delay		; Acquisition Time : See Datasheet "ADC time requirements".
							; Might as well be the best example of Cargo Cult Programming from me.
	

		bsf	    ADCON0, GO	; Start Analog to Digital Conversion.
		btfsc   ADCON0, GO	; Check if the conversion is finished.
		goto    $ - 1
	
		banksel ADRESL
		movf    ADRESL, w             ; Get the ADRESL byte and put it in Voltage_Sensor_ADC
		movwf   Voltage_Sensor_ADC	  ; The value is the digitized analog voltage from the sensor.
									  ; In order to compare it with a temperature, we need to 
									  ; calculate to which temperature in Celsius a certain		
									  ; voltage from the LM35 corresponds. (10mv/°C).
						
									  ; Need to figure out an "easy" way to do multiplication.

	
	;------------------------------------------------------------------------
	;							   Check_High_Threshold
	;------------------------------------------------------------------------

		movlw	d'86'				; That's our High Threshold.
		call	Check_Temperature	; Go to the end of the code
									; in "A Little Story About Conversion" to see
									; where did this value come from.
									; In a nutshell, that's the expected	
									; result a temperature of 42 °C would give 
									; after being converted by a 10 bits , 5 V Vref. ADC.





		btfsc	STATUS, C		; Is the Temperature from the Sensor
								; greater than the High threshold (42°C) ?
								

								
								; sublw works with a two's complement method
								; so 0100 - 0101 = 1111 and no carry.
								; we can obviously rotate to the left through carry (rlf)
								; and btfss, OR let it this way and btfsc.

		goto	Temp_Is_High	; If yes, i.e the temperature is HIGH, then
								; it's time to turn off the heater.
								; If not, i.e the Temperature from the sensor
								; is less than the High threshold and it's more
								; than the Low Threshold, then do nothing.
	


	
	;----------------------------------------------------------------------------
	;								Check_Low_Threshold
	;----------------------------------------------------------------------------

		movlw	d'81'			  ; That's our Low Threshold

		call	Check_Temperature ; In order to know if the temperature from
								  ; the sensor is lesser or greater than our
								  ; threshold, we need to perform a substraction
								  ; the Carry flag in the STATUS register tells us
								  ; something about the sign, thus is it greater or lesser?
		
		btfss	STATUS, C		  ; Is the result of the substraction in Check_Temperature positive?
								  ; If yes, it means that the temperature from the sensor
		goto	Temp_Is_Low		  ; is less than our Low Threshold, hence we need to
								  ; turn the Relay_Pin on. We call the Temp_Is_Low subroutine for that.
	  


Hysteresis

		goto	ADC_Get			; Here land all cases that need to keep the state of the heater.
								; Basically, all cases have been checked (> High Threshold ? < Low Threshold ? Between the two?)
								; And the only thing to do now, is to perform another conversion and
								; start a new cycle.


	;      goto START			TODO: Remove that line at the end if it's not necessary.





	;----------------------------------------------------------------------------------
	;									SUBROUTINES
	;----------------------------------------------------------------------------------



;----------------------------------------------------------

Check_Temperature

		subwf   Voltage_Sensor_ADC, w
	
		return
;----------------------------------------------------------


Temp_Is_Low
		banksel PORTB
		btfss	Relay_Pin	; Is the relay ON ? If it is, then return. (let it that way)
		bsf		Relay_Pin	; If it's OFF, then turn it ON and return.
						
		goto Hysteresis
;-----------------------------------------------------------


Temp_Is_High
		banksel PORTB
		btfsc	Relay_Pin	; Is the Relay OFF? If it is, then return. (let it off).
		bcf		Relay_Pin	; If it's ON, then turn it OFF and return.

		goto Hysteresis
;-----------------------------------------------------------


Delay
		movlw   d'254'          ; A value which will be decremented to lose some time.
	            				; for the ADC converter to function properly.
		movwf   Delay_Counter_1
		decfsz  Delay_Counter_1, f	; Teenage wasteland. Lose some time.
		
		goto    $-1				; Emo stuff...

		return
;-----------------------------------------------------------


;	A Little Story About Conversion


; 	Let's talk about the conversion a bit:
;	Let's say we use Vref = 5V of the supply.
;	The output voltage of the LM35 sensor is
;	Voltage_Sensor = Temperature * 10 / 1000 V
;	Voltage_Sensor = Temperature / 100

;	The result of the conversion of Voltage_Sensor
;	is called Voltage_Sensor_ADC.

;	5 volts input correspond to 255 (8 bits to 1)
;	Voltage_Sensor corresponds to Voltage_Sensor_ADC (or Count)

;	Voltage_Sensor_ADC = Voltage_Sensor * 255 / 5
;	Voltage_Sensor_ADC = Voltage_Sensor * 51

;	Knowing that Voltage_Sensor = Temperature / 100

;	We get:

;	Voltage_Sensor_ADC = (Temperature / 100) * 51

;	Voltage_Sensor_ADC = Temperature * (51/100)	


;	This means that if the Temperature is 100 °C
;	The result of the conversion will be:
;	100 * 51 / 100 = 51.

;	This relation will help translate the "Count" or
;	Voltage_Sensor_ADC to the corresponding temperature.

;	Temperature = Voltage_Sensor_ADC * (100/51)

;	Which means that when we read ADRESL
;	Let's say it has a value or 30.
;	The Temperature which caused this number is
;	Temperature = 30 * (100/51)
;	Which is roughly 58 °C.

; 	Now I get the idea that I can calculate the Count for
;	my two thresholds, and instead of having the PIC compute
;	the corresponding temperature for each acquisition..
;	I'll have it compare each ADRESL with the count corresponding
;	to each threshold.

;	Example: Low_Threshold = 40 °C.
;	Voltage_Sensor_ADC_Low  = 40 * 51 / 100 = 20.4 (we'll worry floating point later).
;	Voltage_Sensor_ADC_High = 42 * 51 / 100 = 21.42.

;	So I'll just match ADRESL with 20 and 21.

;	However ... 20 and 21 are kind of too close.
;	We have two values 2 °C apart (that's huge)
;	Yet, the corresponding counts are very close.
;	That's not good
;	I understand why Ericgibbs and Mr RB insisted on using the whole 10 bits.


;	Maybe use the whole 10 bits like he suggested.

;   5V ---------------> 1023
;	Voltage_Sensor --->	 Voltage_Sensor_ADC

;	Voltage_Sensor_ADC = Voltage_Sensor * 1023 / 5
;	Voltage_Sensor_ADC = (Temperature / 100) * 1023 : 5

;	Voltage_Sensor_ADC = Temperature * 1023 / 500

;	For a temperature of 40 °C:
;	Voltage_Sensor_ADC_Low  = 40 * 1023 / 500 = 81.84
;	Voltage_Sensor_ADC_High = 42 * 1023 / 500 = 85.932

;	They are more spaced, so it's better.
;	I'll use the whole 10 bits.
;	Then match ADRESL with 86 and 81 for the thresholds.

		end
 
hi J.
Your ADC is not set up correctly.
Look at this edited fragment of your code.

The code now returns correctly, 92decimal , 0x5c for an input of 0.45V, in ADRESL

Eric
BTW: Tidied up your CONFIG

Code:
	list		p=16f887	; list directive to define processor
	#include	<p16f887.inc>	; processor specific variable definitions
	errorlevel -302, -207

	__CONFIG    _CONFIG1, _LVP_OFF & _FCMEN_ON & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT
	__CONFIG    _CONFIG2, _WRT_OFF & _BOR21V
													  		  ;Power Up Timer  ON
 
	#define Relay_Pin  PORTB,1
	#define Sensor_Pin PORTA,0
 
 
		cblock 0x20      ; General Purpose Registers start here in a PIC16F887.
 
			Delay_Counter_1, Temporary_Var, Voltage_Sensor_ADC
 
		endc
 
		org 0x00              ;Reset Vector.
 
 
	 	goto Initialize_PIC
 
 
 
Initialize_PIC
 
		banksel TRISA
		bsf	    TRISA, 0    ; Set RA0 (Pin2) to Input 
		banksel ANSEL
		bsf	    ANSEL, 0    ; Set RA0 as Analog .. 
							; Now RA0 is Analog, and is Input.
							; RA0 is ready to receive information 
							; from the sensor.
 
		banksel TRISB
		clrf	TRISB	; Set RB1 to Output.		
 
 
		banksel PORTA
 
		clrf PORTA
		clrf PORTB	; This includes the Relay_Pin (PORTB, 1 - Heater) which is 
					; cleared (turned off) by default for security measures.
		clrf PORTC
 
 
 
		banksel OSCCON
 
		movlw B'01100001'
		movwf OSCCON
 
; OSCCON REGISTER BITS
 
		; - IRCF2 IRCF1 IRCF0 OSTS HTS LTS SCS
		; 0 1     1     0     0    ?   ?   1
 
		; IRCF<2:1> 110 4 MHz (LFINTOSC).
		; OSTS      0   Device is running from Internal Oscillator
		;				(in our case, HFINTOSC).
		; HTS		Is HFINTOSC stable ? (Read)
		; LTS		Is LFINTOSC stable ? (Read)
		; SCS 		1	Internal Oscillator is used for system clock.
 
 
 
		; - : Unimplemented, read as 0.
		  goto START		; Here we go..
 
 
START	 
Initialize_ADC
 
		banksel ADCON1
		movlw   b'10000000'	; Right justified. The ADC has a resolution of 10 bits.
							; We'll read the higher 8 bits in ADRESL since the temperatures
							; we are interested in won't make it to the 9th or 10th bit.
						    ; VDD & VSS for VCFG1 & VCFG0 
		movwf   ADCON1

		banksel TRISA
		bcf 	TRISA,1
		
		banksel ANSEL
		bsf	ANSEL,0
		 
		banksel ADCON0
		movlw   b'10000001'     ; The ADCON0 Register:
							    ; ADCS1 ADCS0 CHS2 CHS1 CHS0 GO_DONE - ADON
 
							    ; ADCS1 ADCS0    : 10 Conversion Clock frequency FOSC/32
							    ;                  To respect TAD time. See PAGE 103.
								;					Shaded cases are eliminated.
 
							    ; CHS2 CHS1 CHS0 : 000   RA0
							    ; GO_DONE        : 0 . Setting it to 1 starts conversion.
								;                  automatically cleared when conversion is done.
							    ; ADON           : 1 . The ADC is enabled.
 
 
		movwf   ADCON0
 
 
ADC_Get
 
 
 
;		call	Delay		; Acquisition Time : See Datasheet "ADC time requirements".
					; Might as well be the best example of Cargo Cult Programming from me.
 
 
		bsf	ADCON0, GO	; Start Analog to Digital Conversion.
		btfsc   ADCON0, GO	; Check if the conversion is finished.
		goto    $ - 1
 
		banksel ADRESL
		movf    ADRESL, w             ; Get the ADRESL byte and put it in Voltage_Sensor_ADC
		movwf   Voltage_Sensor_ADC	  ; The value is the digitized analog voltage from the sensor.
									  ; In order to compare it with a temperature, we need to 
									  ; calculate to which temperature in Celsius a certain		
									  ; voltage from the LM35 corresponds. (10mv/°C).
 
									  ; Need to figure out an "easy" way to do multiplication.
 
Last edited:
After not being able to get it to work and a discussion in Chat, Eric pointed out that the ADCON1 was only 7 bits in my code. I corrected it (in addition to the other corrections) and now it works like a charm.

Thank you so much :)
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top