Applications of AVR single chip controllers AT90S, ATtiny, ATmega and ATxmega A thermometer with an 1-by-8 LCD and an ATtiny24 |

This page includes

- how the measurement goes on, and
- as the temperature-to-ADC relationship is not exactly linear, how to approach the iteration in a square function in assembler, and
- a sophisticated hardware solution for doing all that, and
- an assembler source code for all this.

- to multiply 16-by-16 bit numbers by software (the ATtiny24 does not have a hardware multiplicator),
- to use fixed decimals to avoid floating point math in assembler (to increase the degrees with an additional tenth of degrees),
- to use a square function y = a*x
^{2}+ b*x + c to interpolate non-linear device data, - to increase the accuracy of calculations by applying factors of 256
^{n}, or 2,048 here, to get enough bits for rounding, - to round binary values from 0.45 upwards instead of 0.5, and how
- to adjust a three-factor function either by measuring potentiometer positions or by measuring and calculation.

Top | Sensor | Measuring | Hardware | Algorithms | Software |
---|

```
ldi R16,0b100010|(1<<REFS1) ; Temp measurement, ref voltage = 1.1 V
out ADMUX,R16
```

and you start the ADC and to convert this channel with
```
.equ cAdcClkDiv = (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
ldi R16,(1<<ADEN)|(1<<ADSC)|cAdcClkDiv ; Start ADC and first measurement
out ADCSRA,R16
```

If you then wait until the ADC has finished its work and clears the ADSC bit, you can
read the ADC result with
```
WaitAdc:
sbic ADCSRA,ADSC ; Check ADSC bit
rjmp WaitAdc ; Conversion not yet ready, wait on
in R0,ADCL ; Read LSB of result
in R1,ADCH ; dto., MSB
```

to the register pair R1:R0.
You can now approach the iteration linearly. Either you set the 25°C number to 300 and use this as the constant slope all over the curve or you determine a best-fit linear function.

First of all: to get rid of the Celsius- (and Fahrenheit-) negative numbers we use the absolute temperature in Kelvin (K). °C are converted to Kelvin by adding 273.15, the Fahrenheit-conversion to K is to subtract 32°F, multiply this by 5 / 9 and to add 273.15. That eases all calculations, and we'll get a slope of

If you consider the other two points given additionally you can calculate a best-fit linear function with the following formula:

Above 25°C: ADC = 0.13393 * T (K) + 410.071

Top | Sensor | Measuring | Hardware | Algorithms | Software |
---|

As we are more interested in a temperature calculation from an ADC value, we formulate the function as follows:

This is the square function that we use. In fact it starts at temperatures slightly below 0K, which is physical nonsense. It ends beyond 1,023, where it falls down again (which is also physical nonsense). As the ADC only delivers values between 230 and 370, we keep far away from those physical nonsense areas: only the red part of the curve is used here.

As the handbook provides three ADC/t points, and as the square function has three constants, we can determine those constants from those three points. The picture to the right demonstrates, how the three constants a, b and c can be determined by applying 3-by-3 determinants. You'll find that calculation in the LibreOffice-Calc file here in the spreadsheet "Squared".

The values for the constants in the equation T = a * ADC

a = -0.000510 b = 1.198980 c = -15.625510Use those if you want to convert a single ADC measurement to Kelvin.

As the practical measurements always involves summing up 64 single results, the term 64*ADC, instead of ADC, is used here. The three parameters then are:

a = -1.2456E-06 b = 0.18734 c = -156.2551Note that a and c are both negative, only the linear term with b is positive.

The table from A68 to H97 demonstrates the temperature calculation from the ADC values in details. The line with ADC=300 (the 25°C value) comes to the following contributions:

- Squared portion: -45.9184,
- Linear portion: 359.6939,
- Constant portion: -15.6255.

This is how this function works: while the linear part, b * ADC, increases linearly, the squared function a * ADC

The result is a formula like shown in red in the diagram. It shows a slightly higher T on the mid range around 25°C and slightly lower on both the hot and the cold side.

But: if we use the formula to calculate the temperature on our three points, we end with -1.45° at -40°C and with -1.83° at +85°C, while at +25°C the calculated temperature is by 1.83° too high.

Hence: The logaritmic function is obviously NOT a good approach to interpolate temperatures, if you want to increase your accuracy down to 0.1 K/°C. This is a luck for assembler programmers, that do not like logarithmic functions.

Top | Sensor | Measuring | Hardware | Algorithms | Software |
---|

As you can see, the linear function underestimates the temperature around 25°C and overestimates the temperature below and above the two extreme points. We will later on see in detail how much difference that makes.

So, a short resumee: we'd rather use the square function here to get more accuracy.

Top | Sensor | Measuring | Hardware | Algorithms | Software |
---|

Parameter | Fastest | Slowest |
---|---|---|

Clock frequency | 1 MHz | |

ADC clock prescaler | 2 | 128 |

ADC cycles per conversion | 13 | |

ADC channels to measure | 4 | |

Overall clock divider | 104 | 6,656 |

Measurement frequency | 9.62 kHz | 150 Hz |

The conclusion is, that even with the highest ADC clock prescaler, the measurements are too fast, so that the last digit of the displayed temperature would flicker.

To avoid flickering we can decide to only send the result to the LCD on every one-hundred's measurement. But the second alternative is even better: to sum up the ADC results over a given time and to divide the sum by the number of summed-up measurements. If we use 64 measurements to average those, the measurement frequency comes down to 9.64 kHz / 64 = 150 Hz (ADC clock prescaler = 2) or down to 2.34 Hz (with the prescaler of 128). The lower of the two frequencies is convenient for the human eye, so that the last digit can be read and recognized.

Averaging the ADC results over 64 measurements also increases the accuracy a little bit. It is not a single measurement, in which the last digit flickers. but that flickering of the last digit is summed up. That is nearly as good as a 13th ADC converter bit, so we can undertake to add one decimal digit to the displayed temperature (25.0 instead of 25°C only).

Of course, we do not have to divide the result by 64, which would require six logical shifts to the right as well as six rotates to the right: we can use the 64*ADC result directly to calculate whatever we need to calculate. I'll name that in the following as 64*ADC or shorter as 64ADC.

Of course we do the adding in an interrupt service routine instead of waiting for the cleared ADSC bit. Here is the code for that:

```
; ADCC interrupt service routine
AdccIsr:
in rSreg,SREG ; Save SREG
in rimp,ADCL ; Read LSB from ADC
add rAdcL,rimp ; Add to sum, LSB
in rimp,ADCH ; dto., read MSB
adc rAdcH,rimp ; dto., add MSB to sum
dec rAdcCnt ; Decrease ADC counter
brne AdccIsrRestart
sbr rFlags,1<<bAdc ; Set ADC flag
reti ; Return from interrupt
AdccIsrRestart:
ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|cAdcClkPrsc ; Restart ADC
out ADCSRA,rimp
reti
```

At the end, when the bAdc flag has been set, all 64 measurements have been summed
up and the result of 64*ADC is in rAdcH:rAdcL. The following calculations are
outside the ISR, as a reaction to this flag.
The calculation outside the ISR has additional things to do:

- to clear rAdcH:rAdcL, and
- to set the counter in rAdcCnt to 64, and
- to write the next mux channel to ADMUX, and
- to start the first conversion by writing ADCSRA.

Top | Sensor | Measuring | Hardware | Algorithms | Software |
---|

So, if we convert the 64ADC values to temperatures we use the following formula:

To convert the Kelvin into °C, we just have to subtract 2731.5 (instead of 273.15) from that. The conversion of K to °F is a little bit more complicated, so see below for the details.

Top | Sensor | Measuring | Hardware | Algorithms | Software |
---|

With the numbers of the datasheet, we'll end at

a = -1.24561543367347E-06

b = 0.18734056122449

c = -156.255102040816

Now, the second trick that the assembler programmer often uses to get rid of those float numbers is to multiply those with multiples of 256, which is a simple method (just use one or more bytes at the right side of the result for rounding the higher bytes, by that dividing the result by 256 or higher multiples of 256). The table demonstrates that.

Mult | Value | 256^{n} | Multiplicator | Multiplied value | Rounded |
---|---|---|---|---|---|

a | -1.24561543367347E-06 | 256^{4} | 4,294,967,296 | -5349.87755102041 | -5350 |

b | 0.18734056122449 | 256^{2} | 65,536 | 12277.5510204082 | 12278 |

c | -156.255102040816 | 256^{1} | 256 | -40001.306122449 | -40001 |

Top | Sensor | Measuring | Hardware | Algorithms | Software |
---|

Here you see the calculated curve with the above parameters a, b and c in blue: it looks rather straight, but it isn't. In red color the difference between the squared function and the linear function for the best-fit is displayed on the y axis on the right. As the result of the function is 10*T, the differences of up to +10 at low and high temperatures correspond to 1 K too high, the -16 around the 25°C stands for 1.6 K too low.

From that you see that a linear function is associated with an inaccuracy of +1 and -1.6 degrees. And you won't get it much better.

Slightly better is it to split the curve into two sections, one below and one above 25°C. Now the differences between the squared function and the splitted linear function are by 0.6° different. The difference is relevant at 40 to 50°C and at 0 to -30°C. So, if you want to measure those with a splitted-linear function: do not expect that to be much better than 0.5°. Forget the 0.1° target or use the square function.

Top | Sensor | Measuring | Hardware | Algorithms | Software |
---|

For parameter a this means the multiplier value of 5,350 has to go down by 5%, which is 5,083, as well as up to 5,618. So the whole pot variation spans over 5,618 - 5,083 = 535. If we multiply the 64*ADC by 535 and skip the two lower bytes of the result, we'll have the adder, to be added to 5,083.

In Assembler this would look like this:

```
.equ cPercent = 5 ; Percentage range +/- 5%
; Default middle values for a, b and c from spreadsheet calculation
.equ cMidA = 5350 ; Mid from calculation
.equ cMidB = 12278 ; Mid from calculation
.equ cMidC = 40001 ; Mid from calculation
; Deriving the ranges from that
.equ cLowA = (cMidA * (100 - cPercent) + 50) / 100 ; Low value
.equ cHighA = (cMidA * (100 + cPercent) + 50) / 100 ; High value
.equ cRangeA = cHighA-cLowA ; Range over which A varies
.equ cLowB = (cMidB * (100 - cPercent) + 50) / 100 ; Low value
.equ cHighB = (cMidB * (100 + cPercent) + 50) / 100 ; High value
.equ cRangeB = cHighB-cLowB ; Range over which B varies
.equ cLowC = (cMidC * (100 - cPercent) + 50) / 100 ; Low value
.equ cHighC = (cMidC * (100 + cPercent) + 50) / 100 ; High value
.equ cRangeC = cHighC-cLowC ; Range over which C varies
```

The structure for those values is in the SRAM:
```
.dseg
.org SRAM_START
sParamStruct:
.byte 2 ; cRangeA
.byte 2 ; cLowA
sCa:
.byte 2 ; cA
.byte 2 ; Dummy
.byte 2 ; cRangeB
.byte 2 ; cLowB
sCb:
.byte 2 ; cB
.byte 2 ; Dummy
.byte 2 ; cRangeC
.byte 2 ; cLowC
sCc:
.byte 2 ; cC
.byte 2 ; Dummy
;
.equ ndal = sCa - sParamStruct
.equ ndah = sCa - sParamStruct + 1
.equ ndbl = sCb - sParamStruct
.equ ndbh = sCb - sParamStruct + 1
.equ ndcl = sCc - sParamStruct
.equ ndch = sCc - sParamStruct + 1
```

This structure is written to the flash as a table:
```
ParamTable:
.dw cRangeA, cLowA, cMidA, 0
.dw cRangeB, cLowB, cMidB, 0
.dw cRangeC, cLowC, cMidC, 0
```

By reading the ParamTable with LPM, these 24 bytes are first copied to
the SRAM ParamStruct at startup to init the SRAM space. The register
pair XH:XL serves as channel pointer, the pair YH:YL serves for quick
accesses to this structure. Both are set to ParamStruct at the
beginning.
So whenever a channel's measurement is over, he has

- to transfer the ADC result to the multiplicator M2, and
- to read the next two bytes from SRAM (with the range parameter) to the multiplicator M1 (LSB and MSB), and
- to multiply M1 and M2 as 16-by-16 bit multiplication, and
- to read the next two bytes (with the low value, LSB and MSB) and to add those to bytes 2 and 3 of the multiplication result, and
- to round bytes 2 and 3 of the multiplication result (using byte 0 and 1), and
- to write bytes 2 and 3 of the added and rounded multiplication results to the next two bytes in SRAM (for later use in the temperature calculation).

```
.equ dal = 4
.equ dah = 5
.equ dbl = 12
.equ dbh = 13
.equ dcl = 20
.equ dch = 21
; Accessing a
ldd rM20,Y+dal
ldd rM21,Y+dah
;
; etc.
```

The variations that can be achieved when adjusting are very different. The table shows the degree changes when the parameters at the lowest and the highest range, as compared to the default medium values.

Varying | Low | High | Range |
---|---|---|---|

a | 2.3 | -2.3 | 4.6 |

b | -18.0 | 18.0 | 35.9 |

c | 0.8 | -0.8 | 1.6 |

abc | -14.9 | 14.9 | 29.8 |

Note that varying the a and c potentiometers to lower values increases the displayed temperature.

Of course the highest variation comes with the b trim potentiometer. Here 5 degrees of the 270 mean 0.67 K, so be careful with this. If you do not to use a 10-spin for that you can reduce the variation down to 2%. The variation of the others is less significant.

Top | Sensor | Measuring | Hardware | Algorithms | Software |
---|

- The ATtiny24 measures the temperature by comparison with its internal 1.1V reference voltage, which is coupled to an external capacitor on the AREF pin.
- For the later calculation it first measures the potentiometers on ADC1, ADC2 and ADC3. Those vary the parameters a, b and c by plus/minus 5% of their nominal value. These three potentiometers get their 1.1V over a 11k resistor divider.
- After measuring the three potentiometers the temperature is measured. The ADC result is then multiplied with b, the result is rounded to 24 bits and copied to rT2:rT1:rT0. Additionally the ADC result is multiplied by itself and with a. This result is rounded to 24 bits and is subtracted from rT2:rT1:rT0. After rounding this to 16 bit, 16 bit constant c is subtracted. This is ten-fold of Kelvin now. From that the °C and/or the °F are calculated, if so configured.
- The temperature results are converted to decimal, are formatted and then are displayed on the single-line LCD with eight characters. The LCD uses the upper nibble of the port A as bi-directional data bus and is controlled by the RS, RW and E signals from Port B.
- The ISP6 interface allows to program the device in the system.

After mounting the 100nF cap onto the AREF pin I realized that this reduced the noise from +/-0.3°C down to less than +/-0.1°C.

Here, the complete mounting can be seen. The ISP6 connector programs the controller and serves as operating voltage supplier.

Here are the adjustment pots. Note that the adjustment directions of the a and c constants are in reverse direction to those of the b constant.

Note that the LCD is mounted onto the soldering side of the PCB: the two 7-pin sockets have to be soldered so they point downwards. This allows to access the ATtiny24 thermally and increases heat flow from and to the ATtiny24.

Note that from the ISP6 pins only GND and V

This is how the components sit on the PCB. The ISP6 interface here uses a straight box connector. If you prefer an angled one, you can use that as well.

And don't forget to connect the two 7-pin females to the downside of the PCB.

If you want to increase the PCB's height, e. g. to the standard 40 mm, go ahead. You'll have enough space then to add three or four M2.5-by-20 mm screws on the four sides to attach the PCB to whatever plastic casing.

The operation with rechargeable batteries is associated with higher costs, but after changing empty batteries a few times, you'll arrive at the same cost level.

Top | Sensor | Measuring | Hardware | Algorithms | Software |
---|

The source code needs

- two registers for the multiplicator M1,
- two registers for the multiplicator M2 plus two additional registers to multiply M2 by two (at max 16 times),
- four registers for the result of the multiplication.

```
; Registers
.def rM1L = R0 ; M1, LSB
.def rM1H = R1 ; M1, MSB
.def rM2L = R2 ; M2, LSB
.def rM2H = R3 ; M2, MSB
.def rM22L = R4 ; M2 2 multiplicator, LSB
.def rM22H = R5 ; M2 2 multiplicator, MSB
.def rMR0 = R6 ; Multiplication result, byte 0
.def rMR1 = R7 ; dto., byte 1
.def rMR2 = R8 ; dto., byte 2
.def rMR3 = R9 ; dto., byte 3
; Multiplication 16-by-16 bit
MultM1M2:
clr rM22L ; Clear the upper 16 bits of M2
clr rM22H
clr rMR0 ; Clear result, byte 0
clr rMR1 ; dto., byte 1
clr rMR2 ; dto., byte 2
clr rMR3 ; dto., byte 3
MultM1M2a:
lsr rM1H ; Shift one bit to carry, MSB
ror rM1L ; dto., LSB
brcc MultM1M2b
add rMR0,rM2L ; Add to result
adc rMR1,rM2H
adc rMR2,rM22L
adc rMR3,rM22H
MultM1M2b:
tst rM1L ; Check if M1 is zero, LSB
brne MultM1M2c
tst rM1H ; dto., MSB
breq MultM1M2d
MultM1M2c:
lsl rM2L ; Multiply M2 by 2
rol rM2H
rol rM22L
rol rM22H
rjmp MultM1M2a
MultM1M2d:
ret
```

The results need rounding. Either the complete 4-byte result is rounded to a
16-bit integer or the rounding uses the last byte only and leaves 24 bits of
the result. Note that rounding up takes place if the lower byte is larger than
0.45 (exact in binary math: 0.453125) and not at 0.50.
```
RoundM16:
ldi rmp,0x4D
add rMR0,rmp
ldi rmp,0x8C
adc rMR1,rmp
ldi rmp,0
adc rMR2,rmp
adc rMR3,rmp
ret
RoundM24:
ldi rmp,0x8C
add rMR0,rmp
ldi rmp,0
adc rMR1,rmp
adc rMR2,rmp
adc rMR3,rmp
ret
```

In order to not having to step through all three measuring channels individually, all necessary input data and the space for the output is organized in form of a record of 8 consecutive bytes (of which two are dummies). So each time such a calculation is performed, a pointer in X increases by eight and the next channel to be muxed is calculated from this pointer X: channel = (X - baseaddr) / 8 + 1. The X pointer is restarted to its baseaddress on any temperature measurement, so always starts with channel 1.

When calculating the parameters, the ADMUX port register has to be read. If that was on the temperature measurement channel (in ATtiny24 that is 0b100010), then the temperature calculation has to be performed (see below).

The handling of channel data for a, b and c starts by reading the range from SRAM. These are two bytes to which X points to, they are read to M1 (low and high). The Adc sum in rAdcH:rAdcL is copied to M2 and the multiplication routine is called, followed by a call to RoundM16. Then the low value is read from where X points to and is added to rMR2 and rMR3. Both these registers now hold the multiplicators and are written to the two SRAM locations where X now points to. At the end two is added to X and it points to the next channel's four record entries (all four are words).

The position of X is now tested if it is at the end of the SRAM structure. If so, the next channel to be measured is the temperature channel, and ADMUX is set accordingly. If not, X is converted to the ADMUX channel by

- subtracting ParamStruct from it, and
- dividing that by 8 (three right-shifts), and
- adding one, and
- setting the REFS1 bit with an ORI.

Note that placing all relevant parameters in that row to SRAM relieves us from handling all potentiometer measurements individually.

To calculate the temperature in K, applying the formula

we first multiply rAdcH:rAdcL with the parameter b. We start with the second term, as this is positive (the a and c terms are negative). The 32-bit result of b * 64ADC is rounded down to 24 bits and the three result bytes are copied to the temperature registers rT2:rT1:rT0.

Then rAdcH:rAdcL is multiplied by itself to get 64ADC

At last the parameter c is subtracted from rT2:rT1.

If K is to be displayed (flag bCF = 0), then rT2:rT1 is rounded to 16 bit by using rT0. Please note that the 10-fold of the temperature has been calculated here, so that, at 25.0°C, we now see 2982 in rT2:rT1, to be displayed later on as T=298.2K.

To calculate the temperature in °C we subtract 2731.5 from rT2:rT1:rT0. The minus 0.5 is done by subtracting 0x80 from rT0, the LSB and the MSB of 2731 are then both subtracted with carry (SBCI).

If, after subtracting, the result in rT2:rT1 is positive, we'll clear the T flag, if negative we subtract rT2:rT1 from zero and set the T flag. rT2:rT1 is now the ten-fold of the temperature in °C, e. g. at 25.0°C it is 250 and the T flag is clear.

The calculation of °F from K is a little more complicated. The original formula is:

But, as we have 10*K and as we need 10*F, we'd rather arrive at this formula:

The 1.8 is a real show-stopper. If we multiply it with 256, we get 460.8 or, rounded up 461. If we calculate some temperatures with that, we find that the accuracy is roughly 0.3°F. As the next higher 65,536-fold would not fit into our 16-bit multiplication scheme, we have to use either 1,024 or 2,048 as multiplicator instead of 256. That means either two or three shift operations to divide the result at the end.

In fact we use the 1.8 * 2048 = 3686 or 0x0E66 to multiply the rounded 16-bit 10*K temperature and we use 4596.7 * 1024 = 9414042 or 0x8FA59A as subtractor. If we do that, and round the result down to 16 bits, the result is within rational accuracy.

Of course, the °F can be negative as well, so we do the same procedure as with negative °C (setting the T flag, conversion to a positive value).

Jumper | Open | Closed |
---|---|---|

CF | °C or °F | K |

F | °F | °C |

Note that the conversion of the temperature from K to °C/°F is done above separately.

This is the source code of the conversion of rT2:rT1 to decimal:

```
; Convert temperature in rT2:rT1 to decimal
; X points to the SRAM buffer
ldi XH,High(sDisplay)
ldi XL,Low(sDisplay)
sbrc rFlags,bCF
rjmp Convert2CF
ldi rmp,'T'
st X+,rmp
ldi rmp,'='
st X+,rmp
rjmp ConvertDec
Convert2CF:
ldi rmp,'t'
st X+,rmp
ldi rmp,'='
st X,rmp
ConvertDec:
ldi ZH,High(1000)
ldi ZL,Low(1000)
ldi rmp,'0' - 1
ConvertDec1:
inc rmp
sub rT1,ZL
sbc rT2,ZH
brcc ConvertDec1
add rT1,ZL
adc rT2,ZH
st X+,rmp
ldi ZH,High(100)
ldi ZL,Low(100)
ldi rmp,'0' - 1
ConvertDec2:
inc rmp
sub rT1,ZL
sbc rT2,ZH
brcc ConvertDec2
add rT1,ZL
adc rT2,ZH
st X+,rmp
ldi rmp,'0' - 1
ldi ZL,10
ConvertDec3:
inc rmp
sub rT1,ZL
brcc ConvertDec3
st X+,rmp
ldi rmp,'.'
.if cEN == 0
ldi rmp,','
.endif
st X+,rmp
ldi rmp,'0'+10
add rmp,rT1
st X+, rmp
sbrc rFlags,bCF
rjmp ConvertCF
ldi rmp,'K'
st X,rmp
rjmp ConvertEnd
ConvertCF:
ldi rmp,cDeg
st X+,rmp
ldi rmp,'C'
sbrc rFlags,bF
ldi rmp,'F'
st X,rmp
sbiw XL,6 ; Replace leading zeros
ld rmp,X
cpi rmp,'0'
brne Convert4
ldi rmp,'='
st X+,rmp
ld rmp,X
cpi rmp,'0'
brne Convert4
ldi rmp,' '
st X+,rmp
Convert4: ; Set sign character
brtc Convert5
ldi rmp,'-'
st -X,rmp
rjmp ConvertEnd
Convert5:
.if cPlus == 1
ldi rmp,'+'
st -X,rmp
.endif
ConvertEnd:
```

Top | Sensor | Measuring | Hardware | Algorithms | Software |
---|

Please note that the software uses the LCD include software for accesses to the LCD with the file lcd.inc here, in detail described here, which should be in the same folder when assembling. This include software configures the LCD (in 1-by-8 mode, writes the °-character to the LCD) and configures its pins (data bus and control signals), and writes the result strings to the LCD.

The software has a lot of different debug switches on top. Those have all to be zero, if you want to operate the thermometer.

The debug switches are:

**DebugNoLcd**: this switches all LCD operations off, can be used for simulation,**DebugTCalc**: starts with a completed temperature measurement cycle, so that the calculation of the temperature can be performed without having to wait for 64 ADC conversions, uses the default cMidA, cMidB and cMidC for calculation, the constant DebugAdc can be set to a desired value of a single AD conversion, use the SRAM to see the result, ends in an endless loop,**DebugAbcParam**: simulates a single parameter measurement with selecting the a, b or c channel and the ADC value by which the parameter shall be modified, also ends in an endless loop.

**cMode**: setting this to zero, outputs Kelvin, setting it to one, outputs °C (by default), setting it to two outputs °F. This setting is taken over at start-up to rFlags, later changes to these two bits in rFlags come into effect whenever temperatures are calculated and displayed.**cEN**: setting this to one (by default) uses decimal dots in numbers, zero uses the German notation with a decimal komma.**cPlus**: by setting this to one, the display adds a plus character if the temperature is positive (only in cMode 1 or 2), the default is zero (no plus added).**cAdcClkPresc**: This allows to alter the prescaler of the ADC. Lower prescaler values increase the temperature measurement repetition rate linearly (default: roughly 2 per second).**cManAdj**: This allows manual adjustment of the temperature. The three pot's are not measured and not adjusted, their default mid value is used for temperature calculation. Only every fourth measurement cycle is factually displayed.**cHex**: When in cManAdj mode, this displays the temperature sensor's ADC measurement sum in hex and does not calculate temperatures from that. This mode can be used for adjustment and for calculating a, b and c for a given ATtiny24 from three different temperatures using determinants (see the respective LibreOffice-Calc spread-sheet and the chapter 5.3.2 below). Disable cHex to switch the temperature calculation with the default parameters for a, b and c on again.

Now compare the display on the other two temperatures: if, at the lower of both temperatures, the temperature is too high, while the temperature of the higher of both is also too high, you'll need to increase a by decreasing the a pot to lower pot values.

Repeat these measurements several times to approach your optimal adjustment.

Setting the constant cManAdj in the source code to one enters manual adjustment mode. Only the ADC channel of the thermometer is measured then. It is helpful to set cHex also to 1, so you'll get the 64*ADC sum displayed in hex.

Now measure the ADC sum at three different temperatures. The lowest and the highest should be as far as possible from ambient temperature.

You can enter your measurements (temperature in °C and the resulting 64ADC sum in hex) directly into the green backgrounded cells in the sheet "ManAdj" of the LibreOffice-Calc file here. The sheet then calculates a, b and c from those three points and provides the results as source code lines, to be replaced.

After replacing those lines, remove the 1 from cHex, while leaving cManAdj at one and you see the temperatures related to this adjustment.

To evaluate whether the controller's consumption itself has a considerable influence on the temperature, I packed a plastic cover on top of the DIL package (so heat losses from the chip only occur with the 14 pins of the device), I switched the power on and registered the displayed temperatures. The listed temperatures (see the respective spread-sheet) did not indicate a rising trend with time. Self-heating therefore contributes less than 0.4° to the measured temperature and is not a relevant factor here. It might play a role here that the device enters the sleep mode when not busy, the device is slightly more than by 99% in sleep mode.

Do not try this in C and with a floating point lib, this will only blow up your hardware needs. And it isn't simpler or more accurate than this here.

Top | Sensor | Measuring | Hardware | Algorithms | Software |
---|

©2023 by http://www.avr-asm-tutorial.net