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

Lecture 14: Voltage, current and temperature meter


14.0 Overview

  1. Measuring, calculate and display of voltages
  2. Measuring, calculation and display of currents
  3. Measuring, calculation and display of temperatures

14.1 Measuring, calculate and display of voltages

14.1.1 Hardware

Scheme

Scheme voltage input 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

56k2 resistor 1M resistor 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:

Calculations

In the conversion of the ADC results to the displayed voltage the following parameters play a role: 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 prescaler           128
; 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 rAL= R5 ; ADC result adder, LSB
.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 rRead = R20 ; LCD read register
.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
	rjmp AdcIsr ; ADC ADC Conv Complete
	reti ; EE_RDY EEPROM Ready
	reti ; USI_STR USI START
	reti ; USI_OVF USI Overflow
;
; -------- Interrupt Service Routines --
AdcIsr: ; ADC conversion complete
	in rSreg,SREG ; Save SREG
	in rimp,ADCL ; Read LSB
	add rAL,rimp ; Add to sum
	in rimp,ADCH ; Read MSB
	adc rAH,rimp ; Add to result plus carry
	dec rCtr ; Counter cycles
	brne AdcIsrRet ; Not zero
	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
AdcIsrRet:
	; Start next conversion
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; to ADC control port
	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
	; Start ADC
	ldi rmp,(1<<REFS1)|(1<<MUX1)|(1<<MUX0) ; Int.Ref, channel 3
	out ADMUX,rmp ; to ADC mux port
	clr rAL ; Clear result sum
	clr rAH
	ldi rCtr,64 ; 64 measure cycles
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; Start ADC
	.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 ZH,High(cMult) ; Load constant
	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
	add rR0,rM0 ; Add multiplicator to result
	adc rR1,rM1
	adc rR2,rM2
	adc rR3,rM3
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
	ldi rmp,0x7F ; Add 0x7F
	add rR0,rmp ; to lowest byte
	adc rR1,rmp ; and to second byte with carry
	ldi rmp,0 ; Carry adder
	adc rR2,rmp ; Add carry to third byte
	adc rR3,rmp ; Add carry to fourth byte
	; Display on LCD
	ldi ZH,1 ; LCD position
	ldi ZL,4
	rcall LcdPos
	clt ; Suppress leading zeros
	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
	adc rR3,rR1
	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
	add rmp,rR3 ; Add ASCII-0
	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

Voltage measuring result 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

Scheme current measurement 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

0.1 Ohms 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.

Fuse 2A That is how the 2 A fuse looks like.

Fuse holder And this is a respective fuse holder. To fit to our breadboard we need to solder two short wires to it.

Fuser holder cap 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
;    LCD-RW o--|PB1 ADC1|--o GND
;             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 prescaler           128
; ADC clock             7,812.5 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
; ADC-MUX-Constants
.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 rRead = R20 ; LCD read register
.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
	rjmp AdcIsr ; ADC ADC Conv Complete
	reti ; EE_RDY EEPROM Ready
	reti ; USI_STR USI START
	reti ; USI_OVF USI Overflow
;
; -------- Interrupt Service Routines --
AdcIsr: ; ADC conversion complete
	in rSreg,SREG ; Save SREG
	in rimp,ADCL ; Read LSB ADC
	add rAL,rimp ; Add to sum
	in rimp,ADCH ; Read MSB ADC
	adc rAH,rimp ; Add to sum with carry
	dec rCtr ; Counter measure
	brne AdcIsrRet ; Not zero
	in rimp,ADMUX ; Read Mux port
	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
	out ADMUX,rimp ; in ADMUX port
	rjmp AdcIsrCont ; End of routine
AdcIsrI:
	sbr rFlag,1<<bRdyV ; Set voltage measurement
	ldi rimp,cMuxI ; Start current measurement
	out ADMUX,rimp ; in ADMUX port
AdcIsrCont:
	ldi rCtr,64 ; Restart counter
	mov rM0,rAL ; Transfer measured sum
	mov rM1,rAH
	clr rAL ; Clear sum
	clr rAH
AdcIsrRet:
	; Restart ADC conversion
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; in ADC control port
	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
	; Start ADC
	ldi rmp,cMuxU ; Start voltage measuring
	out ADMUX,rmp ; in ADMUX
	clr rAL ; Clear sum
	clr rAH
	ldi rCtr,64 ; 64 measures
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; Start first ADC conversion
	; 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
	ldi rmp,0x7F ; Adder
	add rR0,rmp ; Round up
	adc rR1,rmp ; Round up with carry
	ldi rmp,0 ; Adder
	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
	clt ; Suppress leading zeros
	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
	adc rR3,rR1
	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
	subi rmp,-'0' ; Add ASCII-0
	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
	add rmp,rR3 ; Add ASCII-0
	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
	ldi rmp,0x7F ; Adder
	add rR0,rmp ; Add last byte
	adc rR1,rmp ; Add pre last byte
	ldi rmp,0 ; Adder
	adc rR2,rmp ; Add carry
	adc rR3,rmp ; Add carry
	; Display on LCD
	clt ; Suppress leading zeros
	ldi ZH,High(2*DecimalTab) ; Point to decimal table
	ldi ZL,Low(2*DecimalTab)
DisplayC1:
	lpm rM2,Z+ ; Read decimal
	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:
	add rR2,rM2 ; Add again
	adc rR3,rM3
	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
	add rmp,rR2
	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
	add rR0,rM0 ; Add multiplicator to result
	adc rR1,rM1
	adc rR2,rM2
	adc rR3,rM3
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

Current measurment 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:
t [°C]ADC
-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
;    LCD-RW o--|PB1 ADC1|--o GND
;             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 prescaler           128
; 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
; ADMUX constants
.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 rRead = R20 ; LCD read register
.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
	rjmp AdcIsr ; ADC ADC Conv Complete
	reti ; EE_RDY EEPROM Ready
	reti ; USI_STR USI START
	reti ; USI_OVF USI Overflow
;
; -------- Interrupt Service Routines --
AdcIsr:
	in rSreg,SREG ; Save SREG
	in rimp,ADCL ; Read ADC LSB
	add rAL,rimp ; Add to sum
	in rimp,ADCH ; Read ADC MSB
	adc rAH,rimp ; Add to sum plus carry
	dec rCtr ; Count measurements
	brne AdcIsrRet ; Not yet zero
	in rimp,ADMUX ; Read ADMUX
	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
	out ADMUX,rimp ; in ADMUX
	rjmp AdcIsrCont
AdcIsrC:
	sbr rFlag,1<<bRdyV ; Set voltage flag 
	ldi rimp,cMuxC ; Start current measurement
	out ADMUX,rimp ; in ADMUX
	rjmp AdcIsrCont
AdcIsrT:
	sbr rFlag,1<<bRdyC ; Set current flag
	ldi rimp,cMuxT ; Start temperature measurement
	out ADMUX,rimp ; in ADMUX
AdcIsrCont:
	ldi rCtr,64 ; Restart counter
	mov rM0,rAL ; Transfer sum
	mov rM1,rAH
	clr rAL ; Clear sum
	clr rAH
AdcIsrRet:
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; Restart ADC measuring
	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
	; Start ADC
	ldi rmp,cMuxV ; Start voltage measuring
	out ADMUX,rmp ; in ADMUX
	clr rAL ; Clear sum
	clr rAH
	ldi rCtr,64 ; 64 measurements
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; Start first ADC conversion
	; 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
	ldi rmp,0x7F ; Adder
	add rR0,rmp ; Round up
	adc rR1,rmp ; Round up with carry
	ldi rmp,0 ; Adder
	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
	clt ; Suppress leading zeros
	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
	adc rR3,rR1
	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
	subi rmp,-'0' ; Add ASCII-0
	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
	add rmp,rR3 ; Add ASCII-0
	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
	ldi rmp,0x7F ; Adder
	add rR0,rmp ; Add last byte
	adc rR1,rmp ; Add pre last byte
	ldi rmp,0 ; Adder
	adc rR2,rmp ; Add carry
	adc rR3,rmp ; Add carry
	; Display on LCD
	clt ; Suppress leading zeros
	ldi ZH,High(2*DecimalTab) ; Point to decimal table
	ldi ZL,Low(2*DecimalTab)
DisplayC1:
	lpm rM2,Z+ ; Read decimal
	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:
	add rR2,rM2 ; Add again
	adc rR3,rM3
	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
	add rmp,rR2
	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
	subi rmp,-'0' ; Add ASCII-0
	cpi rmp,'9'+1 ; A to F?
	brcs LcdHexN1 ; No
	subi rmp,-7 ; Add 7 to adjust to A to F
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,0x7F ; Adder
	add rR0,rmp ; Add to LSB result
	adc rR1,rmp ; Add to MSB result
	ldi rmp,0 ; Adder = 0
	adc rR2,rmp ; Add carry flag to byte 3
	adc rR3,rmp ; Add carry flag to byte 4
	; 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
	ldi rmp,' ' ; Blank instead leading zero
	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
	add rR0,rM0 ; Add multiplicator to result
	adc rR1,rM1
	adc rR2,rM2
	adc rR3,rM3
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

Temperatur measurement This is an example for temperature measurement.

Home Top Voltage Current Temperature


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