CHIPTEMP{10} - An algorithm to calculate the PICaxe Chip temperature

AllyCat

Senior Member
#1
Hi,

Here is my first formal description of an algorithm to accurately calculate the PICaxe's Chip Temperature using only the available on-chip hardware. It's presented as a subroutine within a test harness, for compatibility with PE5, but equally it might be wrtten as a macro and/or embedded within a larger program/subroutine. I've written much on the READINTERNALTEMP commands, for example the bugs (in PE5) and voltage sensitivity, etc., so here I will deal only with a calculation method for the chip temperature using "available" commands.

The complete temperature-measurement process can be divided into four stages:
1. ADC measurement(s) of the supply rail voltage (Vdd),
2. ADC measurement(s) of the on-chip temperature-sensing diodes,
3. Calculation of the temperature (gradient) in the desired units, and
4. Post-processing the result for calibration, filtering and display, etc..

I've already posted several code snippetts to measure the supply rail (e.g. for "10mV" and "1mV" resolution) and written much on the READINTERNALTEMP command. So I won't expand on the options here, but just include a compatible (PE5/PE6) bug-fixed version. Similarly, there are numerous possibilities for post-processing, for example averaging (to reduce spikes or noise), formatting in integer degrees C or F, etc., or even to one decimal place of resolution. Then display the result on the PE terminal (SERTXD..) , on a LCD (SEROUT..) or even via PWM to a DVM , so only a few examples can be shown here.

The algorithm may be optimised for any particular application, but here are just two versions; a basic CHIPTEMP routine and a higher resolution CHIPTEMP10 (named similarly to other PICaxe keywords). However, in this case the "10" suffix indicates that the integer result can be 10 times larger (and thus converted to a "one decimal place" result).

The basic calculation requires two fundamental input parameters, i.e. :
a. The Supply Rail Voltage and
b. A Temperature-related Voltage measurement (from 1. and 2. above).
Also, the temperature coefficient of the on-chip diodes is required (e.g. from a two-point calibration), but in most cases this can be a fixed constant (typically 1.24 mV/degree C for each diode). The algorithm also needs a few "switches", to indicate the number of diodes being used and the number of individual ADC measurements, etc., which are defined by additional program constants.

Last, but definitely not least, the temperature MUST be calibrated for every individual target chip. This is intentionally the last stage in the calculation, which ensures that the calibration is applied directly in integer degrees (or tenths in the case of the "decimal" version). Thus, a single calibration operation is sufficient: The first time the program is run, simply subtract the reported temperature from the actual (room) temperature and apply the numerical value directly to the appropriate "calibration constant", ITZ{10} , within the program.

So here are the two subroutines, with a test harness to demonstrate the integer and decimal versions, and also report the measured supply voltage, on the PE terminal emulator. The raw calculation consists of just three assignments which can be written in three lines of code, however the "10" version is spread over five lines, to accommodate more detailed comments. :

Code:
; CHIPTEMP{10} : A calculation algorithm for the internal chip temperature of M2 PICAXEs.
;  AllyCat,  February 2016.

#picaxe 20m2
#no_data
#terminal 4800

; Variables:
symbol tempb = b1			; Temporary/Local Byte, also for passing paramters etc.
symbol tempw = w1			; Temporary/Local Word, also for passing paramters, etc
symbol Vdd = w2				; PICAXE supply rail voltage in mV
symbol Vadd = w3			; Additional Word variable, sometimes needed for local calculations 

; Calibration Constants:
symbol ITZ10 = 5100		; Calibration offset temperature value in tenths of a degree C
symbol CALVDD20 = 52429		; Calibrate Vdd, nominally 1024 (ADC steps) * 1024 (FVR mV) / 20 (stepsize mV)
symbol CALVDD7 = 59919		; Adjust to calibrate Vdd, nominally 65536 * 32 / 5 / 7 (used in ReadVdd2) 
symbol TEMPCO = 124		; Temperature coefficient of one internal diode (mV/C * 100)
symbol DEMO = 2			; Multiplier to allow decimal resolution to be demonstrated (otherwise = 1)

; Constants for CHIPTEMP10 version (Can give decimal result)
symbol NVDD10 = 2 * DEMO	 	; Number of times Vdd is included (i.e. number of READINTERNALTEMPs)
symbol GRAD12 = 5461333 / TEMPCO	; Voltage / Temperature conversion factor for 12 diodes (655360/12)
symbol SLOPE10 = GRAD12		; Calibration Constant to convert mV into degrees (Fractional value * 65536)

; Constants for CHIPTEMP (integer result) version:
symbol NVDD = 2			; Number of times Vdd is included (i.e. number of READINTERNALTEMPs)
symbol ITZ = ITZ10 / 10		; Calibration temperature corresponding to zero diode voltage (C)
symbol GRAD6 = 1092267 / TEMPCO	; Voltage / Temperature conversion factor for 6 diodes (65536/6)
symbol SLOPE = GRAD6		; Calibration Constant to convert mV into degrees (Fractional value * 65536)

main:
do
; First measure the basic Vdd and Raw Chip Temperature voltages: 	
	call ReadVdd					; Get normal supply voltage in mV
	readinternaltemp IT_RAW_H,0,Vadd		; ADC value (steps) for 4 diodes (2 diodes with PE5)
	fvrsetup %10111010   				; Bugfix for PE5 (selects 4 diodes)
	readinternaltemp IT_RAW_L,0,tempw		; ADC value for 2 diodes (4 diodes with PE5)
	tempw = tempw + Vadd			; ADC value for 6 diodes (with 2 * Vdd measurements)
	Vadd = tempw				; Save value to use with CHIPTEMP10 routine later

; Now the basic integer calculation:				
	call ChipTemp			; Input parameters in tempw & Vdd, result in tempw (or b2)
	sertxd(cr,lf,#tempw,"C ")			; Report the basic integer calculation (CHIPTEMP)

; Now the "High Resolution" version:
	call ReadVdd2				; Higher accuracy measurement of Vdd (in mV)
	sertxd("Vdd= ",#Vdd,"mV ")				; Report the supply voltage		
	tempw = Vadd * DEMO		; Recover value and Multipy to demonstrate decimal result
	Vadd = 0				; Zero unless a FVR was used as an ADC reference voltage
	call ChipTemp10			; Input parameters in tempw,Vdd (& Vadd),result in tempw

; Split the result into decimal parts and report:
	tempb = tempw // 10					; Decimal part of result
	tempw = tempw / 10					; Integer part of result
	sertxd(#tempw,".",#tempb,"C")			; Report the temperature in degrees C
	pause 3000
loop	

ChipTemp10: ; Generic Chip Temperature calculation 
	tempw = tempw * 16 ** Vdd	; Number of ADC steps * Vdd (stepsize) / 4.(Max 4 * ADC values)
	tempw = tempw * 2 + Vadd * 2	; Add the ADC steps referenced to FVR2048 (= mV/2) and double
	tempw = Vdd * NVDD10 - tempw 	; Subtract from sum of Vdds  
	tempw = tempw ** SLOPE10	 	; Scale to degrees (or degrees * 10)
	tempw = ITZ10 - tempw			; Calibrate to reference temperature (e.g. zero C)
	return				; Returns tempw = deg C * 10 (if appropriate constants are used)
	
ChipTemp:
	tempw = tempw * 16 ** Vdd * 4	; Number of ADC steps * Vdd (stepsize), Max 4 * ADC values.
	tempw = Vdd * NVDD - tempw ** SLOPE	 ; Subtract from number of Vdds and Scale to degrees
	tempw = ITZ - tempw			; Calibrate to reference temperature (0C;-40C;-273C,etc.)
	return						; Returns tempw = deg C (or as calibrated) 

ReadVdd:
	adcconfig 0		; Bugfix (omission from CALIBADC) if ADC has been referenced by FVR
	calibadc10 tempw				; Measure FVR1024 relative to Vdd
	Vdd = CALVDD20 / tempw * 20	; Calculate the Vdd in mV
 	Vdd = Vdd + 20 		; Round up the constant to make integer/decimal results more consistent
	return
		
ReadVdd2:
	fvrsetup fvr2048				; Will be used as ADC reference voltage 
	dacsetup $80				; Enable DAC with Vdd reference
	daclevel 10				; Highest to accommodate Vdd = 6v (if FVR = 2048) 
	Vdd = 0					; Prepare to accumulate the Vdd measurement
	for b1 = 1 to 7 				; 7 x samples allows ** to calibrate up to 6000 mV result	
		adcconfig 3			; FVR reference for ADC
		readdac10 tempw			; Note this command uses DAC (not ADC)
		Vdd = Vdd + tempw		; Accumulate scaled Vdd = loops * 1024 * 5 * Vdd / 32 
	next b1
	Vdd = Vdd ** CALVDD7			; Calibrate to mV units (multiplier < 1 )
	return
An explanation of the calculation method will follow in a second post, due to exceeding the forum word limit (again), ;)

Cheers, Alan.
 

AllyCat

Senior Member
#2
Part 2 : How it works.

Hi again,

As with all my software now, I have reserved b1 and w1 as "Temporary" or "Local" variables, and for passing parameters to and from subroutines. Thus in the listing above, "tempw" receives a value from READINTERNALTEMP (or equivalent), passes it on to CHIPTEMP{10} and then receives the temperature result, perhaps to be passed on to a formatting and/or "print" routine. However, Vdd is a sufficiently important variable for the subroutine (and potentially useful elsewhere) that it has a dedicated variable. Some of the modules require an additional temporary/local word variable (Vadd) and for returning a simple integer temperature result, "tempb" may be preferred to "tempw".

1. The first stage of the CHIPTEMP{10} subroutine calculates the voltage (relative to ground) on the (lower end of the) temperature sensing diodes. This is done by multiplying the value returned by the ADC by its "step size" (which is proportional to the supply rail, i.e. stepsize = Vdd / 1024).

In the "10" suffixed version, there is also the opportunity to add in another ADC value (Vadd) measured against a "fixed" ADC stepsize, i.e. referenced to the "Fixed Voltage Reference" (FVR). This "10" version assumes 2mV/step (i.e. the use of FVR2048 divided into 1024 steps), but is dependent on the specific choice of supply voltage.

2. Unfortunately, the on-chip diodes are arranged to "hang down" from the supply rail (Vdd) so the second stage of calculation must subtract the previous measured/calculated voltage from Vdd (multiplied by the number of measurements which were made) to obtain the voltage across the diodes themselves.

The above value is then scaled (to integer degrees or tenths) by a factor determined from the number of diodes and their tempertature coefficient (i.e. degrees per mV). To achieve good accuracy, this uses the ** operator, which requires the effective calibration factor (multiplier) to be less than unity (i.e. no more than 65535 / 65536). Thus a "0.1 degree" resolution result needs an input totalling at least 10 mV/C (or a minimum of 8 diodes, assuming a tempco of 1.25 mV/diode/degreeC).

3. The third stage is to subtract the previously-obtained value from an "offset" constant which represents the (theoretical) temperature when there would be a zero voltage drop across the diodes (typically around 500 degrees C or 900 degrees F ! ). However, this is also the point in the calculation at which a negative result (if appropriate) can arise (it is correctly reported as a "twos complement" value). Therefore, it may be better to adopt a "suppressed zero" reference such as -40 degrees (same in C and F) or -55 degrees C (limit of a DS 18B20 sensor). This is particularly relevant if the data is to be averaged, because 2's complement values cannot be simply summed and divided.


The remainder of the listing is intended as a demonstration/test, not as definitive program code. The problem is that there is a large number of permutations of "candidate" code for the four basic program modules, so it is difficult to define a single "optimum" version (or even several) :

However, a "minimum code size" version does have several unique features (including operation over the entire range of 14/20M2 supply voltages) and happens to give quite reasonable resolution and (post-calibration-) accuracy (within 2 or 3 degrees C) over the full range. It occupies less than 40 bytes of program code space (comparable to a single READNTERNALTEMP _xVx command), but only by "working around" the bugs in PE5 (not when using PE6 because the "bugfixed" code in PE6 is rather inefficient in code size).

So here is my present version which can be usefully put at the top of any (M2) program to give a warning not only of any unexpected reboots, but also of a low supply voltage and perhaps of (previous) excessive power dissipation.

Code:
symbol tempw = w1
symbol Vdd = S_W2
symbol CALVDD20 = 52429		; Adjust to calibrate Vdd, nominally 1024 * 1024 / 20 = 52429
symbol ITZERO = 500		; Adjust to calibrate to room temperature (+/- 1 unit per degree C)
symbol TEMPCO = 124		; Temperature coefficient of the on-chip diodes (mV/C * 100)
symbol SLOPE2 = 3276800 / TEMPCO		; 65536 divided by number of diodes (2) and Tempco (mV/C)

#define USEPE5				; Delete for PE6 or AxePad 1.5.1			
ChipTempMin:				; About 35 bytes with PE5 (excluding the SERTXD output line)
	calibadc10 tempw					; Measure FVR1024 relative to Vdd
	Vdd = CALVDD20 / tempw * 20			; Calculate the Vdd in mV 
#ifdef USEPE5
	readinternaltemp IT_RAW_H,0,tempw		; Read voltage for two diodes
#else
	readinternaltemp IT_RAW_L,0,tempw		; Read voltage for two diodes
#endif
	tempw = tempw * 64 ** Vdd			; Number of ADC steps * Vdd (stepsize)
	tempw = Vdd - tempw ** SLOPE2		; Subtract from Vdd and Scale to degrees
	tempw = ITZERO - tempw			; Calibrate to reference temperature (0C, or -40, etc.)
	sertxd(cr,lf,"Vdd=",#Vdd,"mV,",#w1,"C")	; Report Vdd and Temperature
return
At the other extreme, a "High Resolution" version can achieve around 0.2 degree C resolution, but requires up to 200 bytes of codespace. Note that the "12 diodes" version shown in the demo code above is a "cheat" because simply multiplying the voltage across 6 diodes by 2 does not actually double the measurement resolution. My "real" High Res. version doesn't use the READINTERNALTEMP commands at all, but is coded in an "assembler" fashion (using POKE/PEEKSFR commands). This has a subsidiary advantage that the same program code compiles correctly in both PE5 or PE6, but it is sufficiently complex that a separate thread/post is more appropriate.

Sadly, the "best" construct to achieve an obvious target of 1 degree (C or F) nominal resolution is not easy to define. My new "ReadVdd" routine has simplified the supply voltage measurement for the "medium-accuracy" requirement, but it is still difficult to determine to what extent "averaging" of multiple values can produce a genuine increase in resolution.

In due course, more "complete" versions of program code may be added to this thread, or perhaps some of the others linked above, where appropriate.

Cheers, Alan.
 

AllyCat

Senior Member
#3
Hi,

More than two years on, so maybe it's time for an update and cross-linking to a later related topic. Also, with hindsight, my description above does look rather overwhelming, so perhaps a little more KISS this time. ;)

Hippy has recently devised a method (in post #17 here) which allows a program to automatically detect whether READINTERNALTEMP was generated by the (flawed but compact) PE5 or by PE6. However, the method requires a considerable number of (pre-compiler) #commands (up to ~20 for the generalised version), and still can't discriminate between different compiler versions in AxePad, so it doesn't really meet my ideals. :(

Also, the PE6 "patch" consumes a large number of program bytes, so it seems simpler to just ignore the READINTERNALTEMP command and determine the raw temperature voltage directly, by using just a few SFR commands. Therefore, below is a revised "Universal" program which can employ any version of Editor/Compiler and any supply voltage, either for a general purpose "bootup test header" or in the form of a subroutine.

Code:
; PICaxe Chip temperature measurement for ANY Vdd (<2 - 6 volts)
; AllyCat March 2018
#picaxe 20m2					; Or any other M2
#no_data
; #define MINCODE   				; Saves about 12 bytes of program code

symbol tempw = w1				; Temporary Word variable (byte-addressable)
symbol Vdd = s_w2				; Measured Supply voltage (normally in mV)
symbol CALVDD18 = 58254			; ** Trim value to calibrate Vdd, nominally 1024 * 1024 / 18 = 58254
symbol ITZEROC = 505			; ** Adjust to calibrate to room temperature (+/- 1 unit per degree C)
symbol TEMPCO = 124				; Temperature coefficient of each on-chip diode (mV/C * 100)
symbol SLOPE2C = 3276800 / TEMPCO		; 65536 divided by number of diodes (2) and Tempco (mV/C)
; SFR addresses
symbol ADRESL 	= $3B		     	 	; ADC result Low byte
symbol ADRESH 	= $3C				; ADC High byte
symbol ADCON0 	= $3D				; ADC control register 0
symbol ADCON1   = $3E				; ADC control register 1
symbol FVRCON 	= $57		   	   	; FVR & Temperature sensor Control Register (same as FVRSETUP)

ChipTempU:					; Universal Chip Temperature (sub)routine to use ANY supply voltage 
	calibadc10 tempw			; Measure FVR1024 relative to Vdd [~3 bytes]

#ifdef MINCODE					; Save ~12 program bytes by measuring the Vdd less precisely
	Vdd = CALVDD18 / tempw * 18		; Calculate the Vdd in mV (resolution 18mV = ~2 degC)
#else	  					; [~22 bytes]
	Vdd = tempw / 2 + CALVDD18 / tempw	; Calculate Vdd in steps of 18mV (rounded up by LSb/2)
	calibadc10 tempw			; Measure FVR1024 relative to Vdd again
	Vdd = CALVDD18 / tempw + Vdd * 9	; Merge the Vdds into mV (resolution 9mV = ~1 degC)
#endif

rawinternaltemp_L:  				; [~8 bytes]
	pokesfr ADCON1, %10010000 	   	; Right justify result (0 - 1023); clock/8; REFerence = Vdd
	pokesfr FVRCON, %10100000 		; 2 diode Temperature sensor ON [$20] & (optional) FVR ON [$80]        
;	fvrsetup $A0 	 		 	; Can be used as an alternative to POKESFR FVRCON,$A0
readitemp:  					; [~13 bytes]
	pokesfr ADCON0, %01110101 		; Select Temperature input and Turn ADC On
	pokesfr ADCON0, %01110111 		; Start conversion
	peeksfr ADRESL,WORD tempw		; Read lower and upper bytes into tempw and fall into chiptemp
chiptemp:	  				; [~19 bytes]
	tempw = tempw * 64 ** Vdd		; Number of ADC steps * stepsize (= Vdd / 1024)
	tempw = Vdd - tempw ** SLOPE2C		; Subtract from Vdd and Scale to degrees
	tempw = ITZEROC - tempw			; Calibrate to reference base temperature (0C, or -40, etc.)
; return					; If used as a subroutine
	sertxd(cr,lf,"Vdd=",#Vdd,"mV,",#w1,"C")	; Report Vdd and Temperature [~20 bytes]
Just one compilation option (in addition to the essential calibration stage) which can save about 12 program bytes at the expense of slightly reduced accuracy. The program measures across only two sensing diodes and since the ADC is referenced to the supply rail, the resolution step size is increased at higher supply voltages. For example at Vdd = 5v the ADC resolution is ~5mV/step, but the tempco only 2.5 mV/degree C, so around 2 degrees resolution at best.

Then, the "sensitivity" to the supply rail voltage is about 110 degrees C per volt whilst the best resolution that can be achieved for the Vdd, using a single pass, is 17 mV (i.e. ~2 degrees C): A limiting factor is PICaxe's 16 bit integer division, but a simple trick is to add the "rounded up" value from a second computation (by first adding half of the divisor to the numerator) to double the result. Furthermore, the CALIBADC10 value contains some "noise", particularly at higher Vdds, so it's worthwhile to also repeat this measurement, since it adds only ~3 bytes to the program. Thus the supply rail measurement increments can be halved to around 9 mV, equivalent to an offset of 1 degree C.

Still to come, a few more variations to give an overall resolution of 1 degree C (and perhaps F).

Cheers, Alan.
 

PhilHornby

Senior Member
#4
Have I missed the bit where you describe how to calibrate this?

I get this on a 14M2 :-
Code:
Vdd=4869mV,65524C
The voltage is within the realms of reason, but I can't make out what the temperature reading is trying to tell me :confused:
 

techElder

Well-known member
#5
I heard it was extremely cold there this weekend, but I didn't think it was cold enough for the Celsius scale to do a PICAXE "wraparound!" :D
 

AllyCat

Senior Member
#6
Hi,

Thanks for taking an interest.

Last, but definitely not least, the temperature MUST be calibrated for every individual target chip. This is intentionally the last stage in the calculation, which ensures that the calibration is applied directly in integer degrees (or tenths in the case of the "decimal" version). Thus, a single calibration operation is sufficient: The first time the program is run, simply subtract the reported temperature from the actual (room) temperature and apply the numerical value directly to the appropriate "calibration constant", ITZ{10} , within the program.
It's reporting a (16-bit two's-complement) temperature of -12 degrees C. Update ITZ by adding 12 plus the estimated room temperature and run it again. That's all that should be needed, unlike the PICaxe's READINTERNALTEMP which wraps backwards to different arbitrary values (in the region of 5000) dependent on the voltage used.

As Microchip say in their new Application Note (linked in my "related topic" in #3), "Generally this equation will produce an uncalibrated temperature value that is within 100°C of the actual temperature." ;)

Cheers, Alan.
 
Top