Path: Home ==> AVR-EN ==> Micro beginner ==> 14. Voltage, current and temperature meter     Diese Seite in Deutsch (extern):   # Lecture 14: Voltage, current and temperature meter

## 14.1 Measuring, calculate and display of voltages

### 14.1.1 Hardware

#### Scheme The components for measuring voltages are minimal: a voltage divider that reduces the input voltage and feeds the divided voltage to the ADC3 input pin. With this configuration voltages of up to 20.5 V can be measured. The resistor 56k2 is selected to ensure a short sampling time of the ADC: if the feed resistance is too large, the sampling time is prolonged from 1.5 to two clock cycles of the ADC.

Of course, the experimental board
here can also be used here without modification of the source code.

#### Components  To the left is the resistor of 56.2 kΩ, to the right the 1 MΩ. If you use other values, you will have to change the constants on top of the source code. With that change you can use any resistor combination.

### 14.1.2 Measuring range, measure/calculation/display issues

#### Measuring range

The ADC is programmed to use the internally generated reference voltage of 1.1 V, to be independant from the operating voltage of the ATtiny13. The voltage on the ADC3 pin is UADC3 = UInput * 56.2 / (1000 + 56.2) = 1.1 [V]. The maximum measurable voltage therefore is UInput = 1.1 * (1000 + 56.2) / 56.2 = 20.673 [V]. Per ADC bit the resolution is 0.02 V.

#### Measuring conditions

The software is built on the following conditions:
• The source pin for the ADC is ADC3, which is written to the MUX port of the ATtiny24. As no other channels have to be measured in this version, this does not change.
• As reference for the ADC the internal 1.1 V is used, the results are not depending from the operating voltage.
• The measurements are averaged over 64 single measurements. This results in a measurement/display frequency of 119&nbsl;cs/s.

#### Calculations

In the conversion of the ADC results to the displayed voltage the following parameters play a role:
• the relation of the two resistors, with Rel = (1M+56k2)/56k2,
• at a reference voltage of 1.1 V the 10 bit ADC delivers 1,023, so the measurement result is NADC = Upin * 1024 / 1.1,
• by summing up 64 measurements the ADC yields 64 * NADC as sum value,
• to display the voltage a resolution of 0.01 V makes sense because this is the the ADC resolution of the 10 bit ADC.
From that the following formula for the display applies:

U (0,01V) = (1M+56k2)/56k2 * 1.1 / 1024 * 100 / 64 * NSum-ADC

The first parameters, by which the measured sum has to be muliplied is
U (0.01V) = 0.03154442 * NSum-ADC

To be independant from a floating point math lib, we multiply 0.0315442 with 65,536 and skip the last two bytes of the result (dividing by 65,536). So we come to

U (0.01V) = 2067 * NSum-ADC / 65536

The calculation therefore is a 16-by-16 bit multiplication, with a 32 bit result.

By changing this constant we can accomodate to any other divider relation.

#### Display

The result of the multiplication and the dividing by 65,536 yields values at maximum slightly above 2,000. For converting the binary to ASCII it is sufficient to start with thousands. Suppressing leading zeros shall only apply to 1,000s, to make sure that at least one digit before the descimal point is displayed. The decimal point is to be displayed after the hundreds.

### 14.1.3 Program

The program is rather straight forward, the source code is here. To assemble requires the LCD include file.
``````
;
; ***************************************
; * Measuring voltages with an ATtiny24 *
; * (C)2017 by www.avr-asm-tutorial.net *
; ***************************************
;
.NOLIST
.INCLUDE "tn24def.inc"
.LIST
;
; -------- Switches --------------------
.equ debug = 0 ; Debug switch for Studio simulation
.equ debugN = 1000 ; Parameter for Studio simulation
;
; -------- Hardware --------------------
;   ATtiny24     _______
;             1 /       |14
;       VCC o--|VCC  GND|--o GND
;             2|        |13
;    LCD-RS o--|PB0     |--o NC
;             3|        |12
;    LCD-RW o--|PB1     |--o NC
;             4|        |11
;   VCC-10k o--|RES     |--o NC
;             5|        |10  1 M to Input
;     LCD-E o--|PB2 ADC3|--o 56k2 to GND
;             6|        |9
;    LCD-D7 o--|PA7  PA4|--o LCD-D4
;             7|        |8
;    LCD-D6 o--|PA6  PA5|--o LCD-D5
;              |________|
;
; -------- Timing ----------------------
; Controller clock  1.000.000 cs/s
; ADC clock frequency   7.812.5 cs/s
; ADC clocks per convers.  14.5
; Measure frequency       538.8 cs/s
; Measure cycle (64)        8.42 cs/s
;
; -------- Constants ------------------
.equ cRIn  =   1000000 ; Ohms
.equ cRGnd =     56200 ; Ohms
.equ cMult = ((cRIn+cRGnd)*110+cRGnd/2)/cRGnd
;
; -------- Registers --------------------
; Used: R0 by LCD
; free: R1 .. R4
.def rAH = R6 ; dto., MSB
.def rM0 = R7 ; 32 bit multiplicator
.def rM1 = R8
.def rM2 = R9
.def rM3 = R10
.def rR0 = R11 ; 32 bit result
.def rR1 = R12
.def rR2 = R13
.def rR3 = R14
.def rSreg = R15 ; Save/restore SREG
.def rmp = R16 ; Multi purpose register
.def rimp = R17 ; Multi purpose inside ints
.def rFlag = R18 ; Flag register
.equ bRdy = 0 ; Ready flag
.def rCtr = R19 ; Counter measurements
.def rLine = R21 ; LCD line counter
.def rmo = R22 ; LCD multi purpose
; free: R23 .. R29
; Used: Z = R31:R30 for LCD
;
; -------- Reset and interrupt vectors
rjmp Start ; RESET vector
reti ; INT0 External Int Request 0
reti ; PCINT0 Pin Change Int Request 0
reti ; PCINT1 Pin Change Int Request 1
reti ; WDT Watchdog Time-out
reti ; TIM1_CAPT TC1 Capture Event
reti ; TIM1_COMPA TC1 Compare Match A
reti ; TIM1_COMPB TC1 Compare Match B
reti ; TIM1_OVF TC1 Overflow
reti ; TIM0_COMPA TC0 Compare Match A
reti ; TIM0_COMPB TC0 Compare Match B
reti ; TIM0_OVF TC0 Overflow
reti ; ANA_COMP Analog Comparator
reti ; USI_STR USI START
reti ; USI_OVF USI Overflow
;
; -------- Interrupt Service Routines --
in rSreg,SREG ; Save SREG
dec rCtr ; Counter cycles
sbr rFlag,1<<bRdy ; Set flag
ldi rCtr,64 ; Restart counter
mov rM0,rAL ; Copy sum
mov rM1,rAH
clr rAL ; Clear sum
clr rAH
; Start next conversion
out SREG,rSreg ; Restore SREG
reti
;
; --------- Reset vector, program start
Start:
; Init stack
ldi rmp,LOW(RAMEND) ; Point to RAMEND
out SPL,rmp ; Write to stack pointer
.if debug == 1 ; Debugging session
ldi rmp,Low(debugN*64) ; Simulate N
mov rM0,rmp
ldi rmp,High(debugN*64)
mov rM1,rmp
rjmp Display
.else
; Init LCD
; Init LCD control port
ldi rmp,(1<<bLcdCRE)|(1<<bLcdCRRS)|(1<<bLcdCRRW)
out pLcdCR,rmp ; to LCD control port
clr rmp ; Outputs clear
out pLcdCO,rmp ; to LCD control port
ldi rmp,mLcdDRW ; LCD data port mask write
out pLcdDR,rmp ; to LCD direction port
; Init LCD
rcall LcdInit ; Start LCD
ldi ZH,High(2*LcdTextOut) ; Z to text
ldi ZL,Low(2*LcdTextOut)
rcall LcdText ; Display text
ldi rmp,0x0C ; Cursor and blink off
rcall LcdC4Byte
ldi rmp,(1<<REFS1)|(1<<MUX1)|(1<<MUX0) ; Int.Ref, channel 3
clr rAL ; Clear result sum
clr rAH
ldi rCtr,64 ; 64 measure cycles
.endif
; Sleep mode
ldi rmp,1<<SE ; Sleep Mode Idle
out MCUCR,rmp
; Enable interrupts
sei ; Set int flag
;
; Main program loop
Loop:
sleep ; Go to sleep
nop ; Wake up
sbrc rFlag,bRdy ; Skip next if ready flag clear
rcall Display ; Process ready flag
rjmp Loop
;
; Calculate result and display
Display:
cbr rFlag,1<<bRdy ; Clear ready flag
; Multiply with constant cMult
clr rM2 ; Clear bytes 3 and 4
clr rM3
clr rR0 ; Clear result
clr rR1
clr rR2
clr rR3
ldi ZL,Low(cMult)
Display1:
lsr ZH ; Shift bit from MSB to LSB
ror ZL ; Shift bit from LSB to carry
brcc Display2 ; Bit was clear
Display2:
mov rmp,ZL ; Copy LSB
or rmp,ZH ; Or MSB
breq Display3 ; No more bits to multiply
lsl rM0 ; Multiplicator * 2
rol rM1
rol rM2
rol rM3
rjmp Display1 ; Multiply further
Display3:
; Round result in rR3:rR2
add rR0,rmp ; to lowest byte
adc rR1,rmp ; and to second byte with carry
; Display on LCD
ldi ZH,1 ; LCD position
ldi ZL,4
rcall LcdPos
ldi ZH,High(2*DecimalTab)
ldi ZL,Low(2*DecimalTab)
Display4:
lpm rR0,Z+ ; Read decimal value
lpm rR1,Z+
tst rR0 ; LSB zero?
breq Display8 ; Finished
clr rmp ; rmp is digit counter
Display5:
; Subtract decimal
sub rR2,rR0
sbc rR3,rR1
brcs Display6 ; Carry, end of subtraction
inc rmp ; Increase digit count
rjmp Display5 ; Go on subtracting
Display6:
add rR2,rR0 ; Take back last subtraction
tst rmp ; Digit=0?
brne Display7 ; No, display
brts Display7 ; No suppression of leading zeros
ldi rmp,' ' ; Replace leading zero by blank
rcall LcdD4Byte
set ; Do not suppress leading zeros any more
rjmp Display4 ; Go on displaying digits
Display7:
set ; Do not suppress leading zeros any more
subi rmp,-'0' ; Convert to ASCII
rcall LcdD4Byte
cpi ZL,LOW(2*DecimalTab10) ; Decimal point?
brne Display4 ; No
ldi rmp,'.' ; Display decimal point
rcall LcdD4Byte
rjmp Display4
Display8:
ldi rmp,'0' ; Last digit
rjmp LcdD4Byte
;
; Decimal table thousands and below
DecimalTab:
.dw 1000
.dw 100
DecimalTab10:
.dw 10
.dw 0
;
; --------- LCD routines ---------------
LcdTextOut:
.db " Voltage measuring  ",0x0D,0xFF
.db "U = xx.xx V",0xFE
;        4
; Include LCD routines
.include "Lcd4Busy.inc"
;
;
; End of source code
;
```
```
New in this code is SUB register,register. This subtracts the content of the second register from the first one and stores the result in the first register.

### 14.1.4 Example The internal 1.1 V reference makes it possible: the ATtiny24 measures its own operating voltage.

Home Top Voltage Current Temperature

## 14.2 Measuring, calculation and display of currents

When measuring currents it is crucial to have a as-low-as-possible input resistance to avoid distortions by the measurement. We here use a very special feature of the ADC in newer tiny devices that helps us with that.

### 14.2.1 Hardware

#### Scheme The current is measured via the voltage drop over the resistor of 0.1 Ω. The fuse that is in series with that resistor limits it to 2 A and protects the resistor and the controller input pin ADC2 against short circuitting. The input ADC1 serves as differential input and is grounded. Measured is the differential voltage between ADC2 and ADC1, and this difference is gained by a factor of 20.

#### Components This here is a usual 0.1 Ω resistor. This wire resistor is specified for 5 Watts thermal power, while we would only need P = U * I = (I*R)*I = 4*0.1 = 0.4 W or the next higher rated (0.5 W). Unfortunately those resistors are not sold, so we take what we get. That is how the 2 A fuse looks like. And this is a respective fuse holder. To fit to our breadboard we need to solder two short wires to it. To a fuse holder belongs a fuse holder cap. This is not necessary here because we only measure low voltage DC. So see it rather as dust preventer.

### 14.2.2 Measuring range, measure/calculate/display issues

#### Measuring range

At full swing the ADC input reaches the reference voltage of 1.1 V, divided by the differential gain of 20, which is 0.055 V. With a resistor of 0.1 Ω this corresponds to a current of I = U / R = 0.055 / 0.1 = 0.55 A or 550 mA. Per ADC bit this is a resolution of 0.53 mA. A display resolution of 0.1 mA would be sufficient.

If we would not select a gain of 20 but of 1 (normal ADC input), our full range covers currents of up to I = U / R = 1.1 / 0.1 = 11 A. To cover that whole range our resistor should then have a power of P = I2 * R = 11 * 11 * 0.1 = 12.1 W. Per ADC digit a resolution of around 11 mA would apply. The display should then have a resolution of 0.01 A. And, of course, we need a higher-rated fuse.

#### Measuring

Measuring differential voltages involves just sending a different bit combination to the ADMUX port. The device databook for the ATtiny24 says which MUX bits can be used and lists all those combinations.

When measuring currents, we sum up 64 single values, just like in the previous chapter.

#### Calculation

The measured voltage on the ADC2 input is Umeas [V] = R [Ω] * I [A]. From that I [A] = Umeas [V] / R [Ω]. With a differential gain of 20 I [A] = 20 * Umeas [V] / R [Ω]. With a reference voltage of 1.1 V we see Nmeas = 20 * Umeas * 1024 / 1.1. Also is 20 * Umeas = Nmeas * 1.1 / 1024 and therefore I [A] = Nmess * 1.1 / 1024 / R. For 64 summed up measurements I [A] = NSum-meas * 1.1 / 1024 / R / 64 / 20. For a resolution of 0.0001 A the integer value would be I [0.0001 A] = 10000 * NSum-meas * 1.1 / 1024 / R / 64 / 20 or 0,0083923 * NSum-meas / R. Multiplied by 65,536 a multiplication faktor of 550 results. With 0.1 Ω a multiplication factor of 5,500 results. The whole formula then is I [0.0001 A] = 5,500 * NSum-meas / 65536.

#### Display

The measured sum is a 16 bit binary, which is to be multiplied with the 16 bit binary 5,500. The lower two bytes of the result can be used to round the result in the upper two bytes (division by 65,536). For conversion into the display format of 123.4 mA first the thousands, then the hundreds and the tens are calculated. Following the hundreds no suppression of leading zeros is needed any more, prior to the ones a decimal point is to be displayed.

An alternative display format would be 1.234(5) A. That would require some changes to the source code, but is possible and simple.

### 14.2.3 Program

The program is listed here, the
source code is here. For assembling the LCD include routines are required.
``````
;
; **********************************************
; * To measure and display voltage and current *
; * (C)2017 by www.avr-asm-tutorial.net        *
; **********************************************
;
.NOLIST
.INCLUDE "tn24def.inc"
.LIST
;
; -------- Hardware --------------------
;   ATtiny24     _______
;             1 /       |14
;       VCC o--|VCC  GND|--o GND
;             2|        |13
;    LCD-RS o--|PB0     |--o NC
;             3|        |12
;             4|        |11
;   VCC-10k o--|RES ADC2|--o 0.1 Ohms
;             5|        |10  1 M to input
;     LCD-E o--|PB2 ADC3|--o 56k2 to GND
;             6|        |9
;    LCD-D7 o--|PA7  PA4|--o LCD-D4
;             7|        |8
;    LCD-D6 o--|PA6  PA5|--o LCD-D5
;              |________|
;
; -------- Timing ----------------------
; Processor clock   1,000,000 cs/s
; ADC clocks per conv.     14.5
; Measuring frequency     538.8 cs/s
; Measure cycle, 64 m.      8.42 cs/s
; Two measure cycles        4.21 cs/s
;
; -------- Constants ------------------
; Voltage measurement
.equ cRInp =   1000000 ; Ohm
.equ cRGnd  =     56200 ; Ohm
.equ cMultU = ((cRInp+cRGnd)*110+cRGnd/2)/cRGnd
; Current measurement
.equ cRI = 100 ; Resistor in milli ohm
.equ cMultI = (550000+cRI/2) / cRI
.equ cMuxU = (1<<REFS1)|(1<<MUX1)|(1<<MUX0) ; Voltage input
.equ cMuxI = (1<<REFS1)|0b101101 ; Current input, diff.gain=20
;
; -------- Registers --------------------
; Used: R0 by LCD
; free: R1 .. R4
.def rAL= R5 ; ADC sum LSB
.def rAH = R6 ; ADC sum MSB
.def rM0 = R7 ; 32 bit multiplier
.def rM1 = R8
.def rM2 = R9
.def rM3 = R10
.def rR0 = R11 ; 32 bit result
.def rR1 = R12
.def rR2 = R13
.def rR3 = R14
.def rSreg = R15 ; Save/restore SREG
.def rmp = R16 ; Multi purpose register
.def rimp = R17 ; Multi purpose inside interrupts
.def rFlag = R18 ; Flag register
.equ bRdyV = 0 ; Ready flag voltage
.equ bRdyC = 1 ; Ready flag current
.def rCtr = R19 ; Counter measurements
.def rLine = R21 ; LCD line counter
.def rmo = R22 ; LCD multi purpose
; free: R23 .. R29
; Used: Z = R31:R30 for LCD etc.
;
; -------- Reset and interrupt vectors
rjmp Start ; RESET vector
reti ; INT0 External Int Request 0
reti ; PCINT0 Pin Change Int Request 0
reti ; PCINT1 Pin Change Int Request 1
reti ; WDT Watchdog Time-out
reti ; TIM1_CAPT TC1 Capture Event
reti ; TIM1_COMPA TC1 Compare Match A
reti ; TIM1_COMPB TC1 Compare Match B
reti ; TIM1_OVF TC1 Overflow
reti ; TIM0_COMPA TC0 Compare Match A
reti ; TIM0_COMPB TC0 Compare Match B
reti ; TIM0_OVF TC0 Overflow
reti ; ANA_COMP Analog Comparator
reti ; USI_STR USI START
reti ; USI_OVF USI Overflow
;
; -------- Interrupt Service Routines --
in rSreg,SREG ; Save SREG
dec rCtr ; Counter measure
cpi rimp,cMuxU ; Was voltage measured?
breq AdcIsrI ; Yes, start I measuring
sbr rFlag,1<<bRdyC ; Set ready flag current
ldi rimp,cMuxU ; Start voltage measuring
rjmp AdcIsrCont ; End of routine
sbr rFlag,1<<bRdyV ; Set voltage measurement
ldi rimp,cMuxI ; Start current measurement
ldi rCtr,64 ; Restart counter
mov rM0,rAL ; Transfer measured sum
mov rM1,rAH
clr rAL ; Clear sum
clr rAH
out SREG,rSreg ; Restore SREG
reti
;
; --------- Reset vector, program start
Start:
; Init stack
ldi rmp,LOW(RAMEND) ; Point to RAMEND
out SPL,rmp ; to stack pointer
; Init LCD control port
ldi rmp,(1<<bLcdCRE)|(1<<bLcdCRRS)|(1<<bLcdCRRW)
out pLcdCR,rmp ; to LCD control port
clr rmp ; Control outputs off
out pLcdCO,rmp ; to LCD control port
ldi rmp,mLcdDRW ; LCD data port write
out pLcdDR,rmp ; to LCD data direction port
; Init LCD
rcall LcdInit ; Call init in include
ldi ZH,High(2*LcdTextOut) ; Z to text
ldi ZL,Low(2*LcdTextOut)
rcall LcdText ; Display text
ldi rmp,0x0C ; Cursor and blink off
rcall LcdC4Byte
ldi rmp,cMuxU ; Start voltage measuring
clr rAL ; Clear sum
clr rAH
ldi rCtr,64 ; 64 measures
; Sleep mode
ldi rmp,1<<SE ; Sleep Mode Idle
out MCUCR,rmp
; Enable interrupts
sei ; Set I flag
;
; Main program loop
Loop:
sleep ; Go to sleep
nop ; Wake up
sbrc rFlag,bRdyV ; Skip next if voltage ready flag not set
rcall DisplayV ; Display voltage
sbrc rFlag,bRdyC ; Skip next if current ready flag not set
rcall DisplayC ; Display current
rjmp Loop ; Go back to sleep
;
; Calculate result voltage
DisplayV:
cbr rFlag,1<<bRdyV ; Clear flag
; Multiply with constant cMult
ldi ZH,High(cMultU) ; Constant to Z
ldi ZL,Low(cMultU)
rcall Multiplication ; rM1:rM2 * ZH:ZL
; Round result in rR3:rR2
adc rR1,rmp ; Round up with carry
adc rR2,rmp ; Round up with carry
adc rR3,rmp ; Round up with carry
; Display on LCD
ldi ZH,1 ; Set position on LCD
ldi ZL,4
rcall LcdPos
ldi ZH,High(2*DecimalTab)
ldi ZL,Low(2*DecimalTab)
DisplayV1:
lpm rR0,Z+ ; Read decimal number
lpm rR1,Z+
tst rR0 ; LSB zero?
breq DisplayV5 ; Yes, done
clr rmp ; Digit count
DisplayV2:
sub rR2,rR0 ; Subtract decimal
sbc rR3,rR1
brcs DisplayV3 ; Digit complete
inc rmp ; Increase digit count
rjmp DisplayV2 ; Go on subtracting
DisplayV3:
add rR2,rR0 ; Restore last subtraction
tst rmp ; Digit zero?
brne DisplayV4 ; No
brts DisplayV4 ; No suppression of leading zeros
ldi rmp,' ' ; Suppress leading zero
rcall LcdD4Byte
set ; No suppression of leading zeros any more
rjmp DisplayV1 ; Go on
DisplayV4:
set ; No suppression of leading zeros any more
rcall LcdD4Byte
cpi ZL,LOW(2*DecimalTab10) ; Decimal point?
brne DisplayV1 ; No, go on
ldi rmp,'.' ; Decimal point
rcall LcdD4Byte
rjmp DisplayV1 ; Go on
DisplayV5:
ldi rmp,'0' ; Last digit
rjmp LcdD4Byte
;
; Decimal table thousends
DecimalTab:
.dw 1000
.dw 100
DecimalTab10:
.dw 10
.dw 0
;
; Calculate and display current
DisplayC:
cbr rFlag,1<<bRdyC ; Clear current flag
ldi ZH,2 ; Set LCD position
ldi ZL,4
rcall LcdPos
; Multiply with constant cMultI
ldi ZH,High(cMultI) ; Constant to Z
ldi ZL,Low(cMultI)
rcall Multiplication ; rM1:rM2 * ZH:ZL
; Round result in rR3:rR2:rR1:rR0
; Display on LCD
ldi ZH,High(2*DecimalTab) ; Point to decimal table
ldi ZL,Low(2*DecimalTab)
DisplayC1:
lpm rM3,Z+
tst rM2 ; LSB zero?
breq DisplayC7 ; Yes, done
clr rmp ; Digit counter
DisplayC2:
sub rR2,rM2 ; Subtract decimal
sbc rR3,rM3
brcs DisplayC3 ; Carry: end of subtraction
inc rmp ; Increase digit
rjmp DisplayC2 ; Go on subtracting
DisplayC3:
tst rmp ; Digit zero?
brne DisplayC4 ; No
brts DisplayC5 ; Suppression of leading zeros off
cpi ZL,Low(2*DecimalTab10) ; Decimal point?
breq DisplayC4 ; No
ldi rmp,' ' ; Replace 0 by blank
rcall LcdD4Byte
rjmp DisplayC1 ; Go on
DisplayC4:
set ; Set flag no zero suppression any more
DisplayC5:
subi rmp,-'0' ; Convert to ASCII
rcall LcdD4Byte
rjmp DisplayC1
DisplayC7:
ldi rmp,'.' ; Display decimal point
rcall LcdD4Byte
ldi rmp,'0' ; Display last digit
rjmp LcdD4Byte
;
; 16-by-16 bit multiplication
Multiplication: ; rM1:rM2 * ZH:ZL
; Result in rR3:rR2:rR1:rR0
clr rM2 ; Clear bytes 3 and 4
clr rM3
clr rR0 ; Clear result
clr rR1
clr rR2
clr rR3
Multiplication1:
lsr ZH ; Shift bit from MSB to LSB
ror ZL ; Shift lowest bit to carry
brcc Multiplication2 ; Do not add
Multiplication2:
mov rmp,ZL ; All ones shifted out?
or rmp,ZH
breq Multiplication3 ; Yes, done
lsl rM0 ; Multipliy multiplicator by two
rol rM1
rol rM2
rol rM3
rjmp Multiplication1 ; Continue multiplication
Multiplication3:
ret
;
; --------- LCD routines ---------------
LcdTextOut:
.db "Voltage and current",0x0D
.db "U = xx,xx V",0x0D
.db "I = xxx,x mA",0xFE,0xFF
;        4
;
; LCD include routines
.include "Lcd4Busy.inc"
;
; End of source code
;
```
```

### 14.2.4 Example That is such an example (the German version).

Home Top Voltage Current Temperature

## 14.3 Measuring, calculation and display of temperatures

The ATtiny24 has an internal temperature sensor. This is utilized here.

### 14.3.1 Hardware

For measuring the temperature no external components are necessary.

### 14.3.2 Measuring range, measuring, calculation and display

#### Measuring range

ATMEL provides the following typical measurement results for temperatures:
-40230
25300
85370
Temperatures below -40 and above +85 °C are unpractical and beyond the operating range of the controller.

#### Measuring temperature

The temperature measurement is initiated by setting the ADC multiplexer to 8. This is documented in the device databook and used in the source code.

#### Calculation

From the above listed values the ADC result is Nmeas = 1.1194 * t [°C] + 273.88. From that the temperature calculation is t [°C] = 0.89286 * Nmeas - 244.52. For 64 measurements Messungen and multiplied by 65,536 the following results:

t [°C] = 65536 / 64 * 0.89286 * NSum-meas / 65536 - 245 = 914 * NSum-meas / 65536 - 245.

Factually the parameter 245 is inaccurate and has to be adjusted. To determine this parameter practically, the display of the measured temperature is in hex. This resulted at 21°C in 0x013A, hence 35.8. The temperature was by 15°C too high, the parameter 245 had to be increased by 15.

Who wants it even more accurate has to determine the slope, too, by determining the ADC result at two different temperatures and change the parameter cMultT in the source code accordingly.

The current source code displays the temperature with a resolution of 1°C. For displaying a resolution of 0.1°C has to change the math, e.g. the multiplicator should be 9,143 and the subtractor 2.445 to arrive at tenth of degrees. As the physical accuracy is only 0.89°C, the displayed tenth of degrees pretend a higher accuracy than really is.

#### Display

The displayed temperature is an integer value. It can be positive or negative.

### 14.3.3 Program

The program is listed as follows, the
source code is here and requires the LCD include routines to assemble.

The degree character has to be programmed actively because it is not available with the standard character set of the LCD.
``````
;
; ************************************************
; * Voltage, current and temperature measurement *
; * (C)2017 by www.avr-asm-tutorial.net          *
; ************************************************
;
.NOLIST
.INCLUDE "tn24def.inc"
.LIST
;
; -------- Switch --------------------
.equ debugDisplay = 0 ; Displays temperature in hex
;
; -------- Hardware --------------------
;   ATtiny24     _______
;             1 /       |14
;       VCC o--|VCC  GND|--o GND
;             2|        |13
;    LCD-RS o--|PB0     |--o NC
;             3|        |12
;             4|        |11
;   VCC-10k o--|RES ADC2|--o 0.1 Ohms
;             5|        |10  1 M to input
;     LCD-E o--|PB2 ADC3|--o 56k2 to GND
;             6|        |9
;    LCD-D7 o--|PA7  PA4|--o LCD-D4
;             7|        |8
;    LCD-D6 o--|PA6  PA5|--o LCD-D5
;              |________|
;
; -------- Timing ----------------------
; Clock frequency   1,000,000 cs/s
; ADC clock frequency   7,812.5 Hz
; ADC clocks per measure   14.5
; Measuring frequency     538.8 cs/s
; Measuring cycle (64)      8.42 cs/s
; Three measuring cycles    2.81 cs/s
;
; -------- Constants ------------------
; Voltage
.equ cRInp =   1000000 ; Ohm
.equ cRGnd  =     56200 ; Ohm
.equ cMultU = ((cRInp+cRGnd)*110+cRGnd/2)/cRGnd
; Current
.equ cRI = 100 ; Milliohm
.equ cMultI = (550000+cRI/2) / cRI
; Temperature
.equ cMultT = 914
.equ cMinusT = 245+15 ; Temperature plus correction
.equ cMuxV = (1<<REFS1)|(1<<MUX1)|(1<<MUX0)
.equ cMuxC = (1<<REFS1)|0b101101
.equ cMuxT = (1<<REFS1)|0b100010
;
; -------- Register --------------------
; Used: R0 by LCD
; free: R1 .. R4
.def rAL= R5 ; LSB ADC sum
.def rAH = R6 ; dto., MSB
.def rM0 = R7 ; 32 bit multiplicator
.def rM1 = R8
.def rM2 = R9
.def rM3 = R10
.def rR0 = R11 ; 32 bit result
.def rR1 = R12
.def rR2 = R13
.def rR3 = R14
.def rSreg = R15 ; Save/restore SREG
.def rmp = R16 ; Multi purpose register
.def rimp = R17 ; Multi purpose inside interrupts
.def rFlag = R18 ; Flag register
.equ bRdyV = 0 ; Ready flag voltage
.equ bRdyC = 1 ; Ready flag current
.equ bRdyT = 2 ; Ready flag temperature
.def rCtr = R19 ; Counter for measurements
.def rLine = R21 ; LCD line counter
.def rmo = R22 ; LCD additional multi purpose
; free: R23 .. R29
; Used: Z = R31:R30 for LCD etc.
;
; -------- Reset and interrupt vectors
rjmp Start ; RESET vector
reti ; INT0 External Int Request 0
reti ; PCINT0 Pin Change Int Request 0
reti ; PCINT1 Pin Change Int Request 1
reti ; WDT Watchdog Time-out
reti ; TIM1_CAPT TC1 Capture Event
reti ; TIM1_COMPA TC1 Compare Match A
reti ; TIM1_COMPB TC1 Compare Match B
reti ; TIM1_OVF TC1 Overflow
reti ; TIM0_COMPA TC0 Compare Match A
reti ; TIM0_COMPB TC0 Compare Match B
reti ; TIM0_OVF TC0 Overflow
reti ; ANA_COMP Analog Comparator
reti ; USI_STR USI START
reti ; USI_OVF USI Overflow
;
; -------- Interrupt Service Routines --
in rSreg,SREG ; Save SREG
dec rCtr ; Count measurements
brne AdcIsrRet ; Not yet zero
cpi rimp,cMuxV ; Voltage measured?
breq AdcIsrC ; Yes, start current measurement
cpi rimp,cMuxC ; Current measured?
breq AdcIsrT ; Yes, start temperature measurement
sbr rFlag,1<<bRdyT ; Set T flag, start voltage
ldi rimp,cMuxV ; Measure voltage
sbr rFlag,1<<bRdyV ; Set voltage flag
ldi rimp,cMuxC ; Start current measurement
sbr rFlag,1<<bRdyC ; Set current flag
ldi rimp,cMuxT ; Start temperature measurement
ldi rCtr,64 ; Restart counter
mov rM0,rAL ; Transfer sum
mov rM1,rAH
clr rAL ; Clear sum
clr rAH
out SREG,rSreg ; Restore SREG
reti
;
; --------- Reset vector, program start
Start:
; Init stack
ldi rmp,LOW(RAMEND) ; Point to RAMEND
out SPL,rmp ; to stack pointer
; Init LCD control port
ldi rmp,(1<<bLcdCRE)|(1<<bLcdCRRS)|(1<<bLcdCRRW)
out pLcdCR,rmp ; to LCD control port
clr rmp ; Control outputs off
out pLcdCO,rmp ; to LCD control port
ldi rmp,mLcdDRW ; LCD data port write
out pLcdDR,rmp ; to LCD data direction port
; Init LCD
rcall LcdInit ; Call init in include
ldi ZH,High(2*LcdDefChar) ; Define degree char on LCD
ldi ZL,Low(2*LcdDefChar)
rcall LcdChars ; Define chars
ldi ZH,High(2*LcdTextOut) ; Z to text
ldi ZL,Low(2*LcdTextOut)
rcall LcdText ; Display text
ldi rmp,0x0C ; Cursor and blink off
rcall LcdC4Byte
ldi rmp,cMuxV ; Start voltage measuring
clr rAL ; Clear sum
clr rAH
ldi rCtr,64 ; 64 measurements
; Sleep mode
ldi rmp,1<<SE ; Sleep Mode Idle
out MCUCR,rmp
; Enable interrupts
sei ; Set I flag
;
; Main program loop
Loop:
sleep ; Go to sleep
nop ; Wake up
sbrc rFlag,bRdyV ; Skip next if voltage ready flag not set
rcall DisplayV ; Display voltage
sbrc rFlag,bRdyC ; Skip next if current ready flag not set
rcall DisplayC ; Display current
sbrc rFlag,bRdyT ; Skip next if temperature ready flag not set
rcall DisplayT
rjmp Loop ; Go back to sleep
;
; Calculate result voltage
DisplayV:
cbr rFlag,1<<bRdyV ; Clear flag
; Multiply with constant cMult
ldi ZH,High(cMultU) ; Constant to Z
ldi ZL,Low(cMultU)
rcall Multiplication ; rM1:rM2 * ZH:ZL
; Round result in rR3:rR2
adc rR1,rmp ; Round up with carry
adc rR2,rmp ; Round up with carry
adc rR3,rmp ; Round up with carry
; Display on LCD
ldi ZH,1 ; Set position on LCD
ldi ZL,4
rcall LcdPos
ldi ZH,High(2*DecimalTab)
ldi ZL,Low(2*DecimalTab)
DisplayV1:
lpm rR0,Z+ ; Read decimal number
lpm rR1,Z+
tst rR0 ; LSB zero?
breq DisplayV5 ; Yes, done
clr rmp ; Digit count
DisplayV2:
sub rR2,rR0 ; Subtract decimal
sbc rR3,rR1
brcs DisplayV3 ; Digit complete
inc rmp ; Increase digit count
rjmp DisplayV2 ; Go on subtracting
DisplayV3:
add rR2,rR0 ; Restore last subtraction
tst rmp ; Digit zero?
brne DisplayV4 ; No
brts DisplayV4 ; No suppression of leading zeros
ldi rmp,' ' ; Suppress leading zero
rcall LcdD4Byte
set ; No suppression of leading zeros any more
rjmp DisplayV1 ; Go on
DisplayV4:
set ; No suppression of leading zeros any more
rcall LcdD4Byte
cpi ZL,LOW(2*DecimalTab10) ; Decimal point?
brne DisplayV1 ; No, go on
ldi rmp,'.' ; Decimal point
rcall LcdD4Byte
rjmp DisplayV1 ; Go on
DisplayV5:
ldi rmp,'0' ; Last digit
rjmp LcdD4Byte
;
; Decimal table thousands
DecimalTab:
.dw 1000
.dw 100
DecimalTab10:
.dw 10
.dw 0
;
; Calculate and display current
DisplayC:
cbr rFlag,1<<bRdyC ; Clear current flag
ldi ZH,2 ; Set LCD position
ldi ZL,4
rcall LcdPos
; Multiply with constant cMultI
ldi ZH,High(cMultI) ; Constant to Z
ldi ZL,Low(cMultI)
rcall Multiplication ; rM1:rM2 * ZH:ZL
; Round result in rR3:rR2:rR1:rR0
; Display on LCD
ldi ZH,High(2*DecimalTab) ; Point to decimal table
ldi ZL,Low(2*DecimalTab)
DisplayC1:
lpm rM3,Z+
tst rM2 ; LSB zero?
breq DisplayC7 ; Yes, done
clr rmp ; Digit counter
DisplayC2:
sub rR2,rM2 ; Subtract decimal
sbc rR3,rM3
brcs DisplayC3 ; Carry: end of subtraction
inc rmp ; Increase digit
rjmp DisplayC2 ; Go on subtracting
DisplayC3:
tst rmp ; Digit zero?
brne DisplayC4 ; No
brts DisplayC5 ; Suppression of leading zeros off
cpi ZL,Low(2*DecimalTab10) ; Decimal point?
breq DisplayC4 ; No
ldi rmp,' ' ; Replace 0 by blank
rcall LcdD4Byte
rjmp DisplayC1 ; Go on
DisplayC4:
set ; Set flag no zero suppression any more
DisplayC5:
subi rmp,-'0' ; Convert to ASCII
rcall LcdD4Byte
rjmp DisplayC1
DisplayC7:
ldi rmp,'.' ; Display decimal point
rcall LcdD4Byte
ldi rmp,'0' ; Display last digit
rjmp LcdD4Byte
;
; Calculate temperature
DisplayT:
cbr rFlag,1<<bRdyT ; Clear T flag
.if debugDisplay == 1 ; Debug, display in hex
lsr rM1 ; / 2, divide sum by 64
ror rM0
lsr rM1 ; / 4
ror rM0
lsr rM1 ; / 8
ror rM0
lsr rM1 ; / 16
ror rM0
lsr rM1 ; / 32
ror rM0
lsr rM1 ; / 64
ror rM0
ldi ZH,3 ; Set LCD position
ldi ZL,11
rcall LcdPos
mov rmp,rM1 ; MSB to rmp
rcall LcdHex ; Display in hex
mov rmp,rM0 ; LSB to rmp
rjmp LcdHex ; Display in hex
LcdHex: ; Display rmp in hex
push rmp ; Save rmp
swap rmp ; Upper nibble first
rcall LcdHexN ; Display nibble
pop rmp ; Restore rmp
LcdHexN: ; Display nibble
andi rmp,0x0F ; Mask lower nibble
cpi rmp,'9'+1 ; A to F?
brcs LcdHexN1 ; No
LcdHexN1:
rjmp LcdD4Byte ; Display nibble on LCD
.endif
; Multiply with constant cMultT
ldi ZH,High(cMultT) ; Load constant to Z
ldi ZL,Low(cMultT)
rcall Multiplication ; rM1:rM0 * ZH:ZL
; Round result in rR3:rR2
ldi rmp,0 ; Adder = 0
; Subtract constant cMinusT
ldi rmp,Low(cMinusT)
sub rR2,rmp ; Subtract LSB
ldi rmp,High(cMinusT)
sbc rR3,rmp ; Subtract MSB and carry
; Display LSB on LCD
ldi ZH,3 ; Position on LCD
ldi ZL,4
rcall LcdPos
ldi rmp,'+' ; Positive
tst rR2 ; Test if negative
brpl DisplayT1 ; No, continue
ldi rmp,'-' ; Set negative
neg rR2 ; Subtract from 0xFF
DisplayT1:
rcall LcdD4Byte ; Display sign
ldi ZL,10 ; Decimal 10
clr rmp ; Digit counter
DisplayT2:
sub rR2,ZL ; Subtract decimal
brcs DisplayT3 ; Carry, to end
inc rmp ; Increase digit counter
rjmp DisplayT2 ; Go on subtracting
DisplayT3:
add rR2,ZL ; Take back last subtraction
tst rmp ; Digit = zero?
brne DisplayT4 ; no
rjmp DisplayT5 ; Display
DisplayT4:
subi rmp,-'0' ; To ASCII
DisplayT5:
rcall LcdD4Byte ; Display digit
ldi rmp,'0' ; ASCII-0
add rmp,rR2 ; Convert digit to ASCII
rjmp LcdD4Byte ; Write last digit
;
;
; 16-by-16 bit multiplication
Multiplication: ; rM1:rM2 * ZH:ZL
; Result in rR3:rR2:rR1:rR0
clr rM2 ; Clear bytes 3 and 4
clr rM3
clr rR0 ; Clear result
clr rR1
clr rR2
clr rR3
Multiplication1:
lsr ZH ; Shift bit from MSB to LSB
ror ZL ; Shift lowest bit to carry
brcc Multiplication2 ; Do not add
Multiplication2:
mov rmp,ZL ; All ones shifted out?
or rmp,ZH
breq Multiplication3 ; Yes, done
lsl rM0 ; Multipliy multiplicator by two
rol rM1
rol rM2
rol rM3
rjmp Multiplication1 ; Continue multiplication
Multiplication3:
ret
;
; --------- LCD routines ---------------
LcdTextOut:
.db "Voltage/Current/Temp",0x0D,0xFF
.db "U = xx,xx V",0x0D
.db "I = xxx,x mA",0x0D,0xFF
.db "T = +xx ",0x00,"C",0xFE,0xFF
;        4
; Dregree character
LcdDefChar:
.db 64,0,14,10,14,0,0,0,0,0 ; Z = 0, Grad
.db 0,0 ; End of character table

; LCD include routines
.include "Lcd4Busy.inc"
;
; End of source code
;
```
```
Two new instructions are used here. NEG register subtracts the content of the register from 256 and stores the result in the register. This inverts negative values (bit 7 is set) to their positive value (bit 7 is clear).

The instruction BRPL label branches to the label if the sign during the last operation is cleared (the value was positive).

### 14.3.4 Example This is an example for temperature measurement.

Home Top Voltage Current Temperature