Home ==> Mikrobeginner ==> 14. Spannungs-, Strom- und Temperaturmessgerät
ATtiny24

Lektion 14. Spannungs-, Strom- und Temperaturmessgerät


14.0 Übersicht

  1. Spannungen messen, umrechnen und darstellen
  2. Ströme messen, umrechnen und darstellen
  3. Temperatur messen, umrechnen und darstellen

14.1 Spannungen messen, umrechnen und darstellen

14.1.1 Hardware

Schaltbild

Schaltbild Spannung Der Bauteilebedarf für die Spannungsmessung ist minimal: ein Spannungsteiler, der die Messspannung herabsetzt und die geteilte Spannung an den ADC3-Eingang heranführt. In dieser Konfiguration können bis etwa 20,5 V gemessen werden. Die Größe des 56k2 ergibt sich daraus, dass für das Sampling des ADC keine übermäßige Extrazeit erforderlich wird (bei zu hohem Eingangswiderstand wird der Sampling-Zeitraum von 1,5 Takten auf 2 oder mehr verlängert).

Bauteile

56k2-Widerstand 1M-Widerstand Links ist der Widerstand von 56,2 k abgebildet, rechts der 1M. Die exakte Größe der Widerstände ist unkritisch, weil sie im Kopf der Software beliebig verstellt werden können.

14.1.2 Messbereich, Mess-, Umrechnungs- und Darstellungsvorgang

Messbereich

Bei Vollausschlag erreicht der ADC-Eingang die Referenzspannung 1,1 V. Die Spannung an ADC3 ist UADC3 = UEin * 56,2 / (1000 + 56,2) = 1,1 [V]. Also ist UEin = 1,1 * (1000 + 56,2) / 56,2 = 20,673 [V]. Pro ADC-Bit beträgt die Auflösung 0,02 [V].

Messvorgang

Bei der Messung kommen folgende Bedingungen zum Einsatz:

Umrechnung

In die Umrechnung der Zählergebnisse des ADC gehen folgende Parameter ein: Daraus ergibt sich die anzeigende Spannung (in 0,01 V) zu

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


Die vordersten Parameter, mit denen die ermittelte Summe zu multiplizieren ist, beträgt

U (0,01V) = 0,03154442 * NSum-ADC


Damit wir keine Fließkommabibliothek brauchen, multiplizieren wir den Faktor mit 65536 (und streichen die untersten 16 Bits des Multiplikationsergebnisses einfach weg), also etwa so:

U (0,01V) = 2067 * NSum-ADC / 65536


Damit ist die Umrechnung eine 16-Bit-mal-16-Bit-Aufgabe, mit einem bis zu 32 Bit langen Resultat.

Durch eine geänderte Konstante lassen sich alle beliebigen Teilerverältnisse von Widerständen leicht abbilden.

Darstellungsvorgang

Das Ergebnis der Multiplikation und das Teilen durch 65536 liefern Werte bis knapp über 2.000. Für die Konvertierung in ASCII-Ziffern reicht es also aus, maximal die Tausender zu ermitteln. Die Unterdrückung führender Nullen darf sich nur auf die Tausender auswirken, danach nicht mehr. Das Dezimalkomma ist nach den Hunderten zu setzen.

14.1.3 Programm

Das Programm ist recht straight forward (
zum Quelltext im asm-Format geht es hier, es wird noch die Include-Datei benötigt).

;
; **************************************
; * Spannungen messen mit dem ATtiny24 *
; * (C)2016 by www.gsc-elektronbic.net *
; **************************************
;
.NOLIST
.INCLUDE "tn24def.inc"
.LIST
;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
; -------- Schalter --------------------
.equ debug = 0
.equ debugN = 1000
;
; -------- 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  - Ein 
;     LCD-E o--|PB2 ADC3|--o 56k2 - GND
;             6|        |9
;    LCD-D7 o--|PA7  PA4|--o LCD-D4
;             7|        |8
;    LCD-D6 o--|PA6  PA5|--o LCD-D5
;              |________|
;
; -------- Timing ----------------------
; Taktrate          1.000.000 Hz
; ADC-Vorteiler           128
; ADC-Takt              7.812,5 Hz
; ADC-Takte pro Messg.     14,5
; Messfrequenz            538,8 Hz
; Messzyklus, 64 Messungen  8,42 Hz
;
; -------- Konstanten ------------------
.equ cRE =   1000000 ; Ohm
.equ cRG  =     56200 ; Ohm
.equ cMult = ((cRE+cRG)*110+cRG/2)/cRG
;
; -------- Register --------------------
; frei: R0 .. R4
.def rAL= R5
.def rAH = R6
.def rM0 = R7
.def rM1 = R8
.def rM2 = R9
.def rM3 = R10
.def rE0 = R11
.def rE1 = R12
.def rE2 = R13
.def rE3 = R14
.def rSreg = R15 ; SREG-Sicherung
.def rmp = R16 ; Vielzweckregister
.def rimp = R17 ; Interrupt Vielzweck
.def rFlag = R18 ; Flaggenregister
	.equ bRdy = 0 ; Ready-Flagge
.def rCtr = R19 ; Zaehler Messungen
.def rLese = R20 ; LCD
.def rLine = R21 ; LCD
.def rmo = R22 ; LCD
; frei: R23 .. R29
; verwendet: Z = R31:R30 fuer LCD
;
; -------- Reset- und Interrupt-Vektoren
	rjmp Start ; RESET-Vektor
	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 Routinen --
AdcIsr:
	in rSreg,SREG ; Status retten
	in rimp,ADCL ; Addiere Ergebnis
	add rAL,rimp
	in rimp,ADCH
	adc rAH,rimp ; mit Uebertrag
	dec rCtr ; Zaehler Messwerte
	brne AdcIsrRet ; nicht Null
	sbr rFlag,1<<bRdy
	ldi rCtr,64 ; Neustart Zaehler
	mov rM0,rAL ; Transfer Messwertsumme
	mov rM1,rAH
	clr rAL ; Messwertsumme loeschen
	clr rAH
AdcIsrRet:
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; Starte naechste Messung
	out SREG,rSreg
	reti
;
; --------- Reset-Vektor, Programmstart
Start:
	; Stapel anlegen
	ldi rmp,LOW(RAMEND) ; Ende SRAM
	out SPL,rmp ; in Stapelzeiger
.if debug == 1
	ldi rmp,Low(debugN*64)
	mov rM0,rmp
	ldi rmp,High(debugN*64)
	mov rM1,rmp
	rjmp Ausgabe
	.else
	; LCD Init
	; LCD-Port-Ausgaenge initiieren
	ldi rmp,(1<<bLcdCRE)|(1<<bLcdCRRS)|(1<<bLcdCRRW)
	out pLcdCR,rmp ; Kontrollport-Ausgaenge
	clr rmp ; Ausgaenge aus
	out pLcdCO,rmp ; an Kontrollport
	ldi rmp,mLcdDRW ; Datenport-Ausgabemaske, Schreiben
	out pLcdDR,rmp ; auf Richtungsregister Datenausgabeport
	; LCD Init
	rcall LcdInit ; starten LCD
	ldi ZH,High(2*LcdTextOut) ; Z auf Text
	ldi ZL,Low(2*LcdTextOut)
	rcall LcdText ; Gib Text aus
	ldi rmp,0x0C ; Cursor und Blink aus
	rcall LcdC4Byte
	; ADC starten
	ldi rmp,(1<<REFS1)|(1<<MUX1)|(1<<MUX0) ; Int.Ref, Kanal3
	out ADMUX,rmp
	clr rAL ; Summe loeschen
	clr rAH
	ldi rCtr,64 ; 64 Messungen
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; Starte erste Messung
	.endif
	; Schlafmodus
	ldi rmp,1<<SE ; Sleep Mode Idle
	out MCUCR,rmp
	; Interrupts
	sei ; setze Int-Flagge
	;
; Hauptprogrammschleife
Schleife:
	sleep ; Schlafen
	nop ; Aufwachen
	sbrc rFlag,bRdy ; ueberspringe wenn noch nicht fertig
	rcall Ausgabe
	rjmp Schleife
;
; Ergebnis ausrechnen
Ausgabe:
	cbr rFlag,1<<bRdy ; loesche Flagge
	; mit Konstante cMult multiplizieren
	clr rM2 ; Bytes 3 und 4 loeschen
	clr rM3
	clr rE0 ; Ergebnis loeschen
	clr rE1
	clr rE2
	clr rE3
	ldi ZH,High(cMult) ; Konstante in Z
	ldi ZL,Low(cMult)
Ausgabe1:
	lsr ZH ; Bit aus MSB in LSB
	ror ZL ; niedrigstes Bit in Carry
	brcc Ausgabe2
	add rE0,rM0 ; Multiplikator addieren
	adc rE1,rM1
	adc rE2,rM2
	adc rE3,rM3
Ausgabe2:
	mov rmp,ZL
	or rmp,ZH
	breq Ausgabe3
	lsl rM0 ; Multiplikator mal zwei
	rol rM1
	rol rM2
	rol rM3
	rjmp Ausgabe1 ; weiter multipliz.
Ausgabe3:
	; Runden, Ergebnis in rE3:rE2
	ldi rmp,0x7F
	add rE0,rmp
	adc rE1,rmp
	ldi rmp,0
	adc rE2,rmp
	adc rE3,rmp
	; Auf LCD ausgeben
	ldi ZH,1
	ldi ZL,4
	rcall LcdPos
	clt ; Fuehrende Nullen
	ldi ZH,High(2*DezimalTab)
	ldi ZL,Low(2*DezimalTab)
Ausgabe4:
	lpm rE0,Z+ ; lese Dezimalzahl
	lpm rE1,Z+
	tst rE0 ; LSB Null?
	breq Ausgabe8
	clr rmp
Ausgabe5:
    ; Dezimalzahl abziehen
	sub rE2,rE0
	sbc rE3,rE1
	brcs Ausgabe6 ; fertig
	inc rmp ; weiter abziehen
	rjmp Ausgabe5
Ausgabe6:
	add rE2,rE0 ; wieder addieren
	adc rE3,rE1
	tst rmp ; Null?
	brne Ausgabe7
	brts Ausgabe7
	ldi rmp,' '
	rcall LcdD4Byte
	set
	rjmp Ausgabe4
Ausgabe7:
	set
	subi rmp,-'0'
	rcall LcdD4Byte
	cpi ZL,LOW(2*DezimalTab10)
	brne Ausgabe4
	ldi rmp,',' ; Kommastelle aus
	rcall LcdD4Byte
	rjmp Ausgabe4
Ausgabe8:
	ldi rmp,'0'
	add rmp,rE3
	rjmp LcdD4Byte
;
; Dezimaltabelle Tausender abwaerts
DezimalTab:
.dw 1000
.dw 100
DezimalTab10:
.dw 10
.dw 0
;
; --------- LCD-Routinen ---------------
LcdTextOut:
.db "  Spannungsmessung  ",0x0D,0xFF
.db "U = xx,xx V",0xFE
;        4

.include "Lcd4Busy.inc"
;
;
; Ende Quellcode
;

14.1.4 Messbeispiel

Spannungsmessung So sieht die Anzeige beim Messen der eigenen Betriebsspannung aus.


Home Top Spannungen Ströme Temperatur


14.2 Ströme messen, umrechnen und darstellen

Beim Messen von Strömen kommt es im Gegensatz zur Spanungsmessung darauf an, einen möglichst niedrigen Eingangswiderstand zu haben, um das Messobekt durch die Messschaltung nicht übermässig zu beeinflussen.

14.2.1 Hardware

Schaltbild

Schaltbild Strommessen Gemessen wird der Strom über den Spannungsabfall an dem Widerstand von 0,1 Ω. Die vorgeschaltete Sicherung von 2 A schützt auch den Prozessoreingang gegen Überlastung. Der Eingang ADC1 dient als Differentialeingang, um die Spannung am Messeingang ADC2 um das 20-fache zu verstärken.

Bauteile

0,1 Ohm Das hier ist ein handelsüblicher 0,1 Ω-Widerstand. Eigentlich ist die Leistung von 5 W, die dieser Widerstand kann, bei Weitem überkandidelt. Es gibt aber keine 0,25- oder 1 W-Typen zu kaufen, also nehmen wir dies hier.

Sicherung 2A So sieht die 2A-Sicherung aus.Es tut auch jeder andere Typ oberhalb 550 mA.

Sicherungshalter Das ist ein entsprechender Sicherheitshalter, noch ohne angelötete Drahtstücke für das Breadboard.

Sicherungshalterkappe Und dazu gehört so eine Plastikhaube. Eigentlich hier nicht erforderlich, da wir ja nur im Niederspannungsbereich messen.

14.2.2 Messbereich, Mess-, Umrechnungs- und Darstellungsvorgang

Messbereich

Bei Vollausschlag erreicht der ADC-Eingang die Referenzspannung 1,1 V, geteilt durch die gain von 20 also 0,055 V. Bei einem Widerstand von 0,1 [Ω] entspricht das einem Strom von I = U / R = 0,055 / 0,1 = 0,55 [A] oder 550 [mA]. Pro ADC-Bit ergibt sich eine Auflösung von 550/1024 = 0,53 [mA].

Messung

Wie bei der Spannungsmessung werden auch am Stromeingang 64 Einzelmesswerte aufsummiert und erst dann ausgewertet.

Bei der Messung wird der Diffentialeingang ADC2 - ADC1 verwendet. ADC1 liegt auf 0 V, als Verstärkung der Differenz von (ADC2 - ADC1) wird eine gain von 20 eingestellt.

Der maximal messbare Strom beträgt in dieser Konfiguration 0,55 A, bei einer Auflösung von 0,5 mA/Digit. Will man höhere Ströme messen und anzeigen, dann stellt man den AD-Wandler auf eine Gain von 1 um. Bei gleicher Referenzspannung von 1,1 V kann man dann bis zu 11 A messen, allerdings würde das dann die maximale Leistung des 0,1 Ω-Widerstands mit 12,1 W überschreiten. Unter Einhaltung dieser maximalen Last wären maximal 7 A messbar, als Dauerlast und bei normaler Wärmeabfuhr des Widerstands ca. 3,5 A. Die Messauflösung bei dieser Konfiguration betrüge 11 mA pro Digit, die Umrechnung und Werteausgabe wäre entsprechend anzupassen. Natürlich wäre auch die Absicherung gegen Überschreitung des zulässigen Maximalstroms (Sicherung) entsprechend von 2 A auf 8 A zu ändern.

Umrechnung

Die Messspannung am ADC2-Eingang ist Umess [V] = R [Ω] * I [A], folglich ist I [A] = Umess [V] / R [Ω]. Mit einer Differentialverstärkung von 20, die per Software eingestellt wird, ist I [A] = 20 * Umess [V] / R [Ω]. Bei einer Referenzspannung von 1,1 V ist Nmess = 20 * Umess * 1024 / 1,1. Also ist 20 * Umess = Nmess * 1,1 / 1024 und folglich I [A] = Nmess * 1,1 / 1024 / R. Für 64 aufsummierte Messungen ist I [A] = NSum-mess * 1,1 / 1024 / R / 64 / 20. Für eine Auflösung von 0,0001 A ist I [0,0001 A] = 10000 * NSum-mess * 1,1 / 1024 / R / 64 / 20 oder 0,0083923 * NSum-mess / R. Multipliziert mit 65536 ergibt sich ein Multiplikationsfaktor von 550. Mit 0,1 Ω wird daraus ein Multiplikationsfaktor von 5.500, also I [0,0001 A] = 5.500 * NSum-mess / 65536.

Darstellungsvorgang

Die ermittelte Messwertsumme (16-Bit) wird mit der 16-Bit-Zahl 5.500 multipliziert und die beiden untersten Bytes des Ergebnisses werden zum Runden verwendet. Zur Konvertierung in das Anzeigeformat 123,4 mA werden zunächst die Tausender, dann die Hunderter und Zehner verwendet. Nach den Hundertern werden führende Leerzeichen nicht mehr unterdrückt. Vor der Einer-Ausgabe kommt noch das Komma.

14.2.3 Programm

Das Programm ist hier (
zum Quellcode geht es hier, es wird noch die LCD-Include-Datei benötigt).

;
; **************************************
; * Spannungen und Stroeme messen      *
; * (C)2016 by www.gsc-elektronbic.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 Ohm
;             5|        |10  1 M  - Ein 
;     LCD-E o--|PB2 ADC3|--o 56k2 - GND
;             6|        |9
;    LCD-D7 o--|PA7  PA4|--o LCD-D4
;             7|        |8
;    LCD-D6 o--|PA6  PA5|--o LCD-D5
;              |________|
;
; -------- Timing ----------------------
; Taktrate          1.000.000 Hz
; ADC-Vorteiler           128
; ADC-Takt              7.812,5 Hz
; ADC-Takte pro Messg.     14,5
; Messfrequenz            538,8 Hz
; Messzyklus, 64 Messungen  8,42 Hz
; Zwei Messzyklen, 128 M.   4,21 Hz
;
; -------- Konstanten ------------------
; Spannungsmessung
.equ cRE =   1000000 ; Ohm
.equ cRG  =     56200 ; Ohm
.equ cMultU = ((cRE+cRG)*110+cRG/2)/cRG
; Strommessung
.equ cRI = 100 ; Milliohm
.equ cMultI = (550000+cRI/2) / cRI
; ADC-MUX-Konstanten
.equ cMuxU = (1<<REFS1)|(1<<MUX1)|(1<<MUX0)
.equ cMuxI = (1<<REFS1)|0b101101
;
; -------- Register --------------------
; frei: R0 .. R4
.def rAL= R5
.def rAH = R6
.def rM0 = R7
.def rM1 = R8
.def rM2 = R9
.def rM3 = R10
.def rE0 = R11
.def rE1 = R12
.def rE2 = R13
.def rE3 = R14
.def rSreg = R15 ; SREG-Sicherung
.def rmp = R16 ; Vielzweckregister
.def rimp = R17 ; Interrupt Vielzweck
.def rFlag = R18 ; Flaggenregister
	.equ bRdyU = 0 ; Ready-Flagge Spannung
	.equ bRdyI = 1 ; Ready-Flagge Strom
.def rCtr = R19 ; Zaehler Messungen
.def rLese = R20 ; LCD
.def rLine = R21 ; LCD
.def rmo = R22 ; LCD
; frei: R23 .. R29
; verwendet: Z = R31:R30 fuer LCD
;
; -------- Reset- und Interrupt-Vektoren
	rjmp Start ; RESET-Vektor
	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 Routinen --
AdcIsr:
	in rSreg,SREG ; Status retten
	in rimp,ADCL ; Addiere Ergebnis
	add rAL,rimp
	in rimp,ADCH
	adc rAH,rimp ; mit Uebertrag
	dec rCtr ; Zaehler Messwerte
	brne AdcIsrRet ; nicht Null
	in rimp,ADMUX ; Mux lesen
	cpi rimp,cMuxU ; Wurde U gemessen?
	breq AdcIsrI ; ja, starte I-Messung
	sbr rFlag,1<<bRdyI
	ldi rimp,cMuxU ; nein, starte U-Messung
	out ADMUX,rimp
	rjmp AdcIsrCont
AdcIsrI:
	sbr rFlag,1<<bRdyU
	ldi rimp,cMuxI ; starte I-Messung
	out ADMUX,rimp
AdcIsrCont:
	ldi rCtr,64 ; Neustart Zaehler
	mov rM0,rAL ; Transfer Messwertsumme
	mov rM1,rAH
	clr rAL ; Messwertsumme loeschen
	clr rAH
AdcIsrRet:
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; Starte naechste Messung
	out SREG,rSreg
	reti
;
; --------- Reset-Vektor, Programmstart
Start:
	; Stapel anlegen
	ldi rmp,LOW(RAMEND) ; Ende SRAM
	out SPL,rmp ; in Stapelzeiger
	; LCD-Port-Ausgaenge initiieren
	ldi rmp,(1<<bLcdCRE)|(1<<bLcdCRRS)|(1<<bLcdCRRW)
	out pLcdCR,rmp ; Kontrollport-Ausgaenge
	clr rmp ; Ausgaenge aus
	out pLcdCO,rmp ; an Kontrollport
	ldi rmp,mLcdDRW ; Datenport-Ausgabemaske, Schreiben
	out pLcdDR,rmp ; auf Richtungsregister Datenausgabeport
	; LCD Init
	rcall LcdInit ; starten LCD
	ldi ZH,High(2*LcdTextOut) ; Z auf Text
	ldi ZL,Low(2*LcdTextOut)
	rcall LcdText ; Gib Text aus
	ldi rmp,0x0C ; Cursor und Blink aus
	rcall LcdC4Byte
	; ADC starten
	ldi rmp,cMuxU ; U-Messung starten
	out ADMUX,rmp
	clr rAL ; Summe loeschen
	clr rAH
	ldi rCtr,64 ; 64 Messungen
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; Starte erste Messung
	; Schlafmodus
	ldi rmp,1<<SE ; Sleep Mode Idle
	out MCUCR,rmp
	; Interrupts
	sei ; setze Int-Flagge
	;
; Hauptprogrammschleife
Schleife:
	sleep ; Schlafen
	nop ; Aufwachen
	sbrc rFlag,bRdyU ; ueberspringe wenn noch nicht fertig
	rcall AusgabeU
	sbrc rFlag,bRdyI ; I-Messung fertig?
	rcall AusgabeI
	rjmp Schleife
;
; Ergebnis Spannung ausrechnen
AusgabeU:
	cbr rFlag,1<<bRdyU ; loesche Flagge
	; mit Konstante cMult multiplizieren
	ldi ZH,High(cMultU) ; Konstante in Z
	ldi ZL,Low(cMultU)
	rcall Multiplikation ; rM1:rM2 * ZH:ZL
	; Runden, Ergebnis in rE3:rE2
	ldi rmp,0x7F
	add rE0,rmp
	adc rE1,rmp
	ldi rmp,0
	adc rE2,rmp
	adc rE3,rmp
	; Auf LCD ausgeben
	ldi ZH,1
	ldi ZL,4
	rcall LcdPos
	clt ; Fuehrende Nullen
	ldi ZH,High(2*DezimalTab)
	ldi ZL,Low(2*DezimalTab)
AusgabeU1:
	lpm rE0,Z+ ; lese Dezimalzahl
	lpm rE1,Z+
	tst rE0 ; LSB Null?
	breq AusgabeU5
	clr rmp
AusgabeU2:
    ; Dezimalzahl abziehen
	sub rE2,rE0
	sbc rE3,rE1
	brcs AusgabeU3 ; fertig
	inc rmp ; weiter abziehen
	rjmp AusgabeU2
AusgabeU3:
	add rE2,rE0 ; wieder addieren
	adc rE3,rE1
	tst rmp ; Null?
	brne AusgabeU4
	brts AusgabeU4
	ldi rmp,' '
	rcall LcdD4Byte
	set
	rjmp AusgabeU1
AusgabeU4:
	set
	subi rmp,-'0'
	rcall LcdD4Byte
	cpi ZL,LOW(2*DezimalTab10)
	brne AusgabeU1
	ldi rmp,',' ; Kommastelle aus
	rcall LcdD4Byte
	rjmp AusgabeU1
AusgabeU5:
	ldi rmp,'0'
	add rmp,rE3
	rjmp LcdD4Byte
;
; Dezimaltabelle Tausender abwaerts
DezimalTab:
.dw 1000
.dw 100
DezimalTab10:
.dw 10
.dw 0
;
; Ergebnis Strom ausrechnen
AusgabeI:
	cbr rFlag,1<<bRdyI ; loesche Flagge
	; mit Konstante cMult multiplizieren
	ldi ZH,2
	ldi ZL,4
	rcall LcdPos
	ldi ZH,High(cMultI) ; Konstante in Z
	ldi ZL,Low(cMultI)
	rcall Multiplikation ; rM1:rM2 * ZH:ZL
	; Runden, Ergebnis in rE3:rE2:rE1
	ldi rmp,0x7F
	add rE0,rmp
	adc rE1,rmp
	ldi rmp,0
	adc rE2,rmp
	adc rE3,rmp
	; Auf LCD ausgeben
	clt ; Fuehrende Nullen
	ldi ZH,High(2*DezimalTab)
	ldi ZL,Low(2*DezimalTab)
AusgabeI1:
	lpm rM2,Z+ ; lese Dezimalzahl
	lpm rM3,Z+
	tst rM2 ; LSB Null?
	breq AusgabeI7
	clr rmp
AusgabeI2:
    ; Dezimalzahl abziehen
	sub rE2,rM2
	sbc rE3,rM3
	brcs AusgabeI3 ; fertig
	inc rmp ; weiter abziehen
	rjmp AusgabeI2
AusgabeI3:
	add rE2,rM2 ; wieder addieren
	adc rE3,rM3
	tst rmp ; Null?
	brne AusgabeI4
	brts AusgabeI5
	cpi ZL,Low(2*Dezimaltab10)
	breq AusgabeI4
	ldi rmp,' '
	rcall LcdD4Byte
	rjmp AusgabeI1
AusgabeI4:
	set
AusgabeI5:
	subi rmp,-'0'
	rcall LcdD4Byte
	rjmp AusgabeI1
AusgabeI7:
	ldi rmp,',' ; Kommastelle aus
	rcall LcdD4Byte
	ldi rmp,'0'
	add rmp,rE2
	rjmp LcdD4Byte
;
; 16- * 16-Bit Multiplikation
Multiplikation: ; rM1:rM2 * ZH:ZL
; Ergebnis in rE3:rE2:rE1:rE0
	clr rM2 ; Bytes 3 und 4 loeschen
	clr rM3
	clr rE0 ; Ergebnis loeschen
	clr rE1
	clr rE2
	clr rE3
Multiplikation1:
	lsr ZH ; Bit aus MSB in LSB
	ror ZL ; niedrigstes Bit in Carry
	brcc Multiplikation2
	add rE0,rM0 ; Multiplikator addieren
	adc rE1,rM1
	adc rE2,rM2
	adc rE3,rM3
Multiplikation2:
	mov rmp,ZL
	or rmp,ZH
	breq Multiplikation3
	lsl rM0 ; Multiplikator mal zwei
	rol rM1
	rol rM2
	rol rM3
	rjmp Multiplikation1 ; weiter multipliz.
Multiplikation3:
	ret
;
; --------- LCD-Routinen ---------------
LcdTextOut:
.db "Spannung+Strom tn24",0x0D
.db "U = xx,xx V",0x0D
.db "I = xxx,x mA",0xFE,0xFF
;        4
;
; LCD-Include-Routinen
.include "Lcd4Busy.inc"
;
; Ende Quellcode
;

14.2.4 Messbeispiel

Strommessung Das ist ein solches Messbeispiel.

Home Top Spannungen Ströme Temperatur


14.3 Temperaturen messen, umrechnen und darstellen

14.3.1 Hardware

Für die Messung der Temperatur sind keine externen Bauelemente erforderlich.

14.3.2 Messbereich, Mess-, Umrechnungs- und Darstellungsvorgang

Messbereich

ATMEL gibt für folgende Temperaturen typische ADC-Messergebnisse an:
t [°C]ADC
-40230
25300
85370

Messvorgang

Die Temperaturmessung wird durch Umschaltung des Multiplexers auf Kanal 8 eingeschaltet. Der entsprechende Umschaltbefehl ist im Handbuch angegeben und im Quelltext verwendet.

Umrechnung

Aus den tabellierten Messwerten ergibt sich für Nmess = 1,1194 * t [°C] + 273,88. Umgekehrt ergibt sich daraus für t [°C] = 0,89286 * Nmess - 244,52. Für 64 Messungen und multipliziert mit 65536 ergibt sich

t [°C] = 65536 / 64 * 0,89286 * NSum-mess / 65536 - 245 = 914 * NSum-mess / 65536 - 245.


Faktisch ist der Parameter 245 ungenau und muss justiert werden. Um diese Zahl praktisch zu bestimmen, ist im Programm die Ausgabe der gemessenen Temperatur in hex auf dem Display vorgesehen. Diese ergab z.B. bei 21°C 0x013A, also 35,8. Die Temperatur war also um 15°C zu hoch und der Parameter 245 ist folglich um 15 zu erhöhen.

Wer es noch genauer haben möchte, muss auch die Steigung durch Messung bei zwei verschiedenen Temperaturen neu bestimmen und den Parameter cMultT im Programm entsprechend anpassen.

Wer die Temperatur auf 0,1 °C genau ausgeben will, muss den Multiplikationsfaktor und den abzuziehenden Betrag ändern. So kommt z.B. eine Multiplikation der Messwertsumme mit 9.143 bei einer Subtraktion von 2.445 infrage, um Zehntelgrad zu erhalten (unter Division des Multiplikationsergebnisses mit 65.536). Da die physikalische Auflösung der Messung aber nur bei 0,89 °C liegt, sind die dann angezeigten Zehntelgrad-Werte arg ungenaue Schätzungen. Jedes moderne Hightech-Handy ist da genauer, sofern es nicht gerade eben erst aus der Hosentasche gezogen wurde und eine mehr oder weniger gelungene Mischung aus Körper- und Umgebungstemperatur anzeigt.

Darstellung

Die dargestellte Temperatur ist ein ganzzahliger Wert, genauer ist die Messung nicht. Es können positive und negative Temperaturen vorkommen.

14.3.3 Programm

Das Programm ist hier (
zum Quellcode im asm-Format geht es hier, es wird noch die LCD-Include-Routine benötigt.

;
; *****************************************
; * Spannung, Strom und Temperatur messen *
; * (C)2016 by www.gsc-elektronic.net     *
; *****************************************
;
.NOLIST
.INCLUDE "tn24def.inc"
.LIST
;
; -------- Schalter --------------------
.equ debugDisplay = 0 ; zeigt Temperatur
;                       messung in hex an
;
; -------- 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 Ohm
;             5|        |10  1 M  - Ein 
;     LCD-E o--|PB2 ADC3|--o 56k2 - GND
;             6|        |9
;    LCD-D7 o--|PA7  PA4|--o LCD-D4
;             7|        |8
;    LCD-D6 o--|PA6  PA5|--o LCD-D5
;              |________|
;
; -------- Timing ----------------------
; Taktrate          1.000.000 Hz
; ADC-Vorteiler           128
; ADC-Takt              7.812,5 Hz
; ADC-Takte pro Messg.     14,5
; Messfrequenz            538,8 Hz
; Messzyklus, 64 Messungen  8,42 Hz
; Drei Messzyklen, 192 M.   2,81 Hz
;
; -------- Konstanten ------------------
; Spannungsmessung
.equ cRE =   1000000 ; Ohm
.equ cRG  =     56200 ; Ohm
.equ cMultU = ((cRE+cRG)*110+cRG/2)/cRG
; Strommessung
.equ cRI = 100 ; Milliohm
.equ cMultI = (550000+cRI/2) / cRI
; Temperaturmessung
.equ cMultT = 914
.equ cMinusT = 245+15 ; Temperaturkorrektur
; ADC-MUX-Konstanten
.equ cMuxU = (1<<REFS1)|(1<<MUX1)|(1<<MUX0)
.equ cMuxI = (1<<REFS1)|0b101101
.equ cMuxT = (1<<REFS1)|0b100010
;
; -------- Register --------------------
; frei: R0 .. R4
.def rAL= R5
.def rAH = R6
.def rM0 = R7
.def rM1 = R8
.def rM2 = R9
.def rM3 = R10
.def rE0 = R11
.def rE1 = R12
.def rE2 = R13
.def rE3 = R14
.def rSreg = R15 ; SREG-Sicherung
.def rmp = R16 ; Vielzweckregister
.def rimp = R17 ; Interrupt Vielzweck
.def rFlag = R18 ; Flaggenregister
	.equ bRdyU = 0 ; Ready-Flagge Spannung
	.equ bRdyI = 1 ; Ready-Flagge Strom
	.equ bRdyT = 2 ; Ready-Flagge Temperatur
.def rCtr = R19 ; Zaehler Messungen
.def rLese = R20 ; LCD
.def rLine = R21 ; LCD
.def rmo = R22 ; LCD
; frei: R23 .. R29
; verwendet: Z = R31:R30 fuer LCD
;
; -------- Reset- und Interrupt-Vektoren
	rjmp Start ; RESET-Vektor
	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 Routinen --
AdcIsr:
	in rSreg,SREG ; Status retten
	in rimp,ADCL ; Addiere Ergebnis
	add rAL,rimp
	in rimp,ADCH
	adc rAH,rimp ; mit Uebertrag
	dec rCtr ; Zaehler Messwerte
	brne AdcIsrRet ; nicht Null
	in rimp,ADMUX ; Mux lesen
	cpi rimp,cMuxU ; Wurde U gemessen?
	breq AdcIsrI ; ja, starte I-Messung
	cpi rimp,cMuxI ; Wurde I gemessen?
	breq AdcIsrT ; ja, starte T-Messung
	sbr rFlag,1<<bRdyT ; nein, starte U-Messung
	ldi rimp,cMuxU
	out ADMUX,rimp
	rjmp AdcIsrCont
AdcIsrI:
	sbr rFlag,1<<bRdyU
	ldi rimp,cMuxI ; starte I-Messung
	out ADMUX,rimp
	rjmp AdcIsrCont
AdcIsrT:
	sbr rFlag,1<<bRdyI
	ldi rimp,cMuxT ; starte T-Messung
	out ADMUX,rimp
AdcIsrCont:
	ldi rCtr,64 ; Neustart Zaehler
	mov rM0,rAL ; Transfer Messwertsumme
	mov rM1,rAH
	clr rAL ; Messwertsumme loeschen
	clr rAH
AdcIsrRet:
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; Starte naechste Messung
	out SREG,rSreg
	reti
;
; --------- Reset-Vektor, Programmstart
Start:
	; Stapel anlegen
	ldi rmp,LOW(RAMEND) ; Ende SRAM
	out SPL,rmp ; in Stapelzeiger
	; LCD-Port-Ausgaenge initiieren
	ldi rmp,(1<<bLcdCRE)|(1<<bLcdCRRS)|(1<<bLcdCRRW)
	out pLcdCR,rmp ; Kontrollport-Ausgaenge
	clr rmp ; Ausgaenge aus
	out pLcdCO,rmp ; an Kontrollport
	ldi rmp,mLcdDRW ; Datenport-Ausgabemaske, Schreiben
	out pLcdDR,rmp ; auf Richtungsregister Datenausgabeport
	; LCD Init
	rcall LcdInit ; starten LCD
	ldi ZH,High(2*LcdDefChar)
	ldi ZL,Low(2*LcdDefChar)
	rcall LcdChars
	ldi ZH,High(2*LcdTextOut) ; Z auf Text
	ldi ZL,Low(2*LcdTextOut)
	rcall LcdText ; Gib Text aus
	ldi rmp,0x0C ; Cursor und Blink aus
	rcall LcdC4Byte
	; ADC starten
	ldi rmp,cMuxU ; U-Messung starten
	out ADMUX,rmp
	clr rAL ; Summe loeschen
	clr rAH
	ldi rCtr,64 ; 64 Messungen
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; Starte erste Messung
	; Schlafmodus
	ldi rmp,1<<SE ; Sleep Mode Idle
	out MCUCR,rmp
	; Interrupts
	sei ; setze Int-Flagge
	;
; Hauptprogrammschleife
Schleife:
	sleep ; Schlafen
	nop ; Aufwachen
	sbrc rFlag,bRdyU ; ueberspringe wenn noch nicht fertig
	rcall AusgabeU
	sbrc rFlag,bRdyI ; I-Messung fertig?
	rcall AusgabeI
	sbrc rFlag,bRdyT ; T-Messung fertig?
	rcall AusgabeT
	rjmp Schleife
;
; Ergebnis Spannung ausrechnen
AusgabeU:
	cbr rFlag,1<<bRdyU ; loesche Flagge
	; mit Konstante cMult multiplizieren
	ldi ZH,High(cMultU) ; Konstante in Z
	ldi ZL,Low(cMultU)
	rcall Multiplikation ; rM1:rM2 * ZH:ZL
	; Runden, Ergebnis in rE3:rE2
	ldi rmp,0x7F
	add rE0,rmp
	adc rE1,rmp
	ldi rmp,0
	adc rE2,rmp
	adc rE3,rmp
	; Auf LCD ausgeben
	ldi ZH,1
	ldi ZL,4
	rcall LcdPos
	clt ; Fuehrende Nullen
	ldi ZH,High(2*DezimalTab)
	ldi ZL,Low(2*DezimalTab)
AusgabeU1:
	lpm rE0,Z+ ; lese Dezimalzahl
	lpm rE1,Z+
	tst rE0 ; LSB Null?
	breq AusgabeU5
	clr rmp
AusgabeU2:
    ; Dezimalzahl abziehen
	sub rE2,rE0
	sbc rE3,rE1
	brcs AusgabeU3 ; fertig
	inc rmp ; weiter abziehen
	rjmp AusgabeU2
AusgabeU3:
	add rE2,rE0 ; wieder addieren
	adc rE3,rE1
	tst rmp ; Null?
	brne AusgabeU4
	brts AusgabeU4
	ldi rmp,' '
	rcall LcdD4Byte
	set
	rjmp AusgabeU1
AusgabeU4:
	set
	subi rmp,-'0'
	rcall LcdD4Byte
	cpi ZL,LOW(2*DezimalTab10)
	brne AusgabeU1
	ldi rmp,',' ; Kommastelle aus
	rcall LcdD4Byte
	rjmp AusgabeU1
AusgabeU5:
	ldi rmp,'0'
	add rmp,rE3
	rjmp LcdD4Byte
;
; Dezimaltabelle Tausender abwaerts
DezimalTab:
.dw 1000
.dw 100
DezimalTab10:
.dw 10
.dw 0
;
; Ergebnis Strom ausrechnen
AusgabeI:
	cbr rFlag,1<<bRdyI ; loesche Flagge
	; mit Konstante cMult multiplizieren
	ldi ZH,2
	ldi ZL,4
	rcall LcdPos
	ldi ZH,High(cMultI) ; Konstante in Z
	ldi ZL,Low(cMultI)
	rcall Multiplikation ; rM1:rM2 * ZH:ZL
	; Runden, Ergebnis in rE3:rE2:rE1
	ldi rmp,0x7F
	add rE0,rmp
	adc rE1,rmp
	ldi rmp,0
	adc rE2,rmp
	adc rE3,rmp
	; Auf LCD ausgeben
	clt ; Fuehrende Nullen
	ldi ZH,High(2*DezimalTab)
	ldi ZL,Low(2*DezimalTab)
AusgabeI1:
	lpm rM2,Z+ ; lese Dezimalzahl
	lpm rM3,Z+
	tst rM2 ; LSB Null?
	breq AusgabeI7
	clr rmp
AusgabeI2:
    ; Dezimalzahl abziehen
	sub rE2,rM2
	sbc rE3,rM3
	brcs AusgabeI3 ; fertig
	inc rmp ; weiter abziehen
	rjmp AusgabeI2
AusgabeI3:
	add rE2,rM2 ; wieder addieren
	adc rE3,rM3
	tst rmp ; Null?
	brne AusgabeI4
	brts AusgabeI5
	cpi ZL,Low(2*Dezimaltab10)
	breq AusgabeI4
	ldi rmp,' '
	rcall LcdD4Byte
	rjmp AusgabeI1
AusgabeI4:
	set
AusgabeI5:
	subi rmp,-'0'
	rcall LcdD4Byte
	rjmp AusgabeI1
AusgabeI7:
	ldi rmp,',' ; Kommastelle aus
	rcall LcdD4Byte
	ldi rmp,'0'
	add rmp,rE2
	rjmp LcdD4Byte
;
; Ergebnis Temperatur ausrechnen
AusgabeT:
	cbr rFlag,1<<bRdyT ; loesche Flagge
.if debugDisplay == 1
	lsr rM1 ; / 2
	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
	ldi ZL,11
	rcall LcdPos
	mov rmp,rM1
	rcall LcdHex
	mov rmp,rM0
	rjmp LcdHex
LcdHex:
	push rmp
	swap rmp
	rcall LcdHexN
	pop rmp
LcdHexN:
	andi rmp,0x0F
	subi rmp,-'0'
	cpi rmp,'9'+1
	brcs LcdHexN1
	subi rmp,-7
LcdHexN1:
	rjmp LcdD4Byte
	.endif
	; mit Konstante cMult multiplizieren
	ldi ZH,High(cMultT) ; Konstante in Z
	ldi ZL,Low(cMultT)
	rcall Multiplikation ; rM1:rM0 * ZH:ZL
	; Runden, Ergebnis in rE3:rE2
	ldi rmp,0x7F
	add rE0,rmp
	adc rE1,rmp
	ldi rmp,0
	adc rE2,rmp
	adc rE3,rmp
	; Konstante abziehen
	ldi rmp,Low(cMinusT)
	sub rE2,rmp
	ldi rmp,High(cMinusT)
	sbc rE3,rmp
	; Auf LCD ausgeben
	ldi ZH,3
	ldi ZL,4
	rcall LcdPos
	ldi rmp,'+'
	tst rE2
	brpl AusgabeT1
	ldi rmp,'-'
	neg rE2
AusgabeT1:
	rcall LcdD4Byte
	ldi ZL,10
	clr rmp
AusgabeT2:
	sub rE2,ZL
	brcs AusgabeT3
	inc rmp
	rjmp AusgabeT2
AusgabeT3:
	add rE2,ZL
	tst rmp
	brne AusgabeT4
	ldi rmp,' '
	rjmp AusgabeT5
AusgabeT4:
	subi rmp,-'0'
AusgabeT5:
	rcall LcdD4Byte
	ldi rmp,'0'
	add rmp,rE2
	rjmp LcdD4Byte
;
; 16- * 16-Bit Multiplikation
Multiplikation: ; rM1:rM2 * ZH:ZL
; Ergebnis in rE3:rE2:rE1:rE0
	clr rM2 ; Bytes 3 und 4 loeschen
	clr rM3
	clr rE0 ; Ergebnis loeschen
	clr rE1
	clr rE2
	clr rE3
Multiplikation1:
	lsr ZH ; Bit aus MSB in LSB
	ror ZL ; niedrigstes Bit in Carry
	brcc Multiplikation2
	add rE0,rM0 ; Multiplikator addieren
	adc rE1,rM1
	adc rE2,rM2
	adc rE3,rM3
Multiplikation2:
	mov rmp,ZL
	or rmp,ZH
	breq Multiplikation3
	lsl rM0 ; Multiplikator mal zwei
	rol rM1
	rol rM2
	rol rM3
	rjmp Multiplikation1 ; weiter multipliz.
Multiplikation3:
	ret
;
; --------- LCD-Routinen ---------------
LcdTextOut:
.db "Spannung/Strom/Temp.",0x0D,0xFF
.db "U = xx,xx V",0x0D
.db "I = xxx,x mA",0x0D,0xFF
.db "T = +xx ",0x00,"C",0xFE,0xFF 
;        4
; Gradzeichen
LcdDefChar:
.db 64,0,14,10,14,0,0,0,0,0 ; Z = 0, Grad
.db 0,0 ; Ende der Tabelle

; LCD-Include-Routinen
.include "Lcd4Busy.inc"
;
; Ende Quellcode
;

14.3.4 Messbeispiel

Temperaturmessung Das ist ein Beispiel für eine Temperaturmessung.

Home Top Spannungen Ströme Temperatur


©2016 by http://www.gsc-elektronic.net