Pfad:
Home ==>
Mikrobeginner ==> 14. Spannungs-, Strom- und Temperaturmessgerät
This page in English (external):
Lektion 14. Spannungs-, Strom- und Temperaturmessgerät
14.0 Übersicht
- Spannungen messen, umrechnen und darstellen
- Ströme messen, umrechnen und darstellen
- Temperatur messen, umrechnen und darstellen
14.1.1 Hardware
14.1.1.1 Schaltbild
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).
14.1.1.2 Bauteile
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.1.3 Alternativer Aufbau
Wer dieses oder die nächsten Experimente lieber auf kompaktere Weise
aufbauen möchte und die vielen Zuleitungen zur LCD fest verdrahtet
sehen möchte, kann sich das
Board hier
als gedruckte Platine bauen. Alle Programmbeispiele dieser und der
nachfolgenden Lektionen laufen darauf ohne Änderungen.
14.1.2 Messbereich, Mess-, Umrechnungs- und Darstellungsvorgang
14.1.2.1 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].
14.1.2.2 Messvorgang
Bei der Messung kommen folgende Bedingungen zum Einsatz:
- Als Quelle wird beim Init der ADC3 im MUX-Port der ADC eingestellt. Da keine anderen
Kanäle gemessen werden, bleibt es dauerhaft dabei.
- Als Referenz für den ADC wird die interne Referenzspannung von 1,1 V verwendet.
Das macht die Messergebnisse unabhängig von der Betriebsspannung.
- Die Messungen werden über 64 Einzelmessungen gemittelt. Das ergibt stabile
Ergebnisse und eine Messfreuenz von 119 Hz.
14.1.2.3 Umrechnung
In die Umrechnung der Zählergebnisse des ADC gehen folgende Parameter ein:
- Das Verhältnis der beiden Widerstände mit U = (1M+56k2)/56k2 * Umess.
- Bei einer Spannung von 1,1 V liefert der ADC als Ergebnis 1024, also ist
NADC = Umess * 1024 / 1,1.
- Durch das Aufsummieren von 64 Messergebnissen liefert der ADC 64 * NADC als
Summenwert.
- Als Darstellungsauflösung werden Hundertstel Volt (0,01 V) gewählt. Bei
Vollausschlag von 20 V entspricht das der erzielbaren Genauigkeit der Messung mit dem
10-Bit-ADC.
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
; ; -------- Programmablauf --------------
;
; Die analoge Spannung am ADC3-Eingang wird
; fortlaufend gemessen und ueber 64 Messungen
; gemittelt. Das gemittelte Messergebnis wird
; mit dem Widerstandsverhaeltnis multipli-
; ziert und auf der LCD dezimal ausgegeben.
;
; -------- 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 --
;
; ADC Ready Interrupt
; wird nach dem Abschluss der AD-Wandlung
; ausgeloest
;
; Summiert das Wandlungsergebnis auf und
; zaehlt den Zaehler rCtr abwaerts. Ist
; dieser Null wird die bRdy-Flagge gesetzt.
; Die nachste Wandlung wird angestossen.
;
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
;
; Routine Ausgabe
; wird von der bRdy-Flagge ausgeloest
;
; Das Ergebnis der 64 Wandlungen wird mit
; der Konstante cMult multipliziert, in
; dezimales Format umgewandelt und auf der
; LCD ausgegeben.
;
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
So sieht die Anzeige beim Messen der eigenen Betriebsspannung aus.
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
14.2.1.1 Schaltbild
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.
14.2.1.2 Bauteile
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.
So sieht die 2A-Sicherung aus.Es tut auch jeder andere Typ oberhalb 550 mA.
Das ist ein entsprechender Sicherheitshalter, noch ohne angelötete Drahtstücke
für das Breadboard.
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
14.2.2.1 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].
14.2.2.2 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.
14.2.2.3 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.
14.2.2.4 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
;
; -------- Programmablauf --------------
;
; Nacheinander werden die Spannungen an
; den AD-Eingaengen
; a) ADC3 mit dem Spannungsteiler, und
; b) ADC2 und ADC1 als Differenzwert mit
; dem Stromshunt
; je 64 mal gemessen und gemittelt und
; danach in eine Spannung (a) oder einen
; Stromwert (b) umgerechnet und auf der
; LCD ausgegeben.
;
; -------- 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 --
;
; ADC Ready Interrupt
; wird nach jeder erfolgten AD-Wandlung
; ausgeloest
;
; Addiert den Messwert zur Summe, zaehlt
; den rCtr abwaerts. Erreicht er Null,
; wird er Null wird der gegenteilige
; Messkanal im Multiplexer eingestellt.
; Wurde zuletzt U gemessen wird der
; Summenwert in die U-Zwischenregister
; kopiert und die bRdyU-Flagge gesetzt.
; Wurde I gemessen, wird der Summenwert
; in das I-Zwischenregister kopiert und
; die Flagge bRdyI gesetzt.
; In allen Faellen wird die naechste
; Wandlung gestartet.
;
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
;
; Routine AusgabeU
; wird von der Flagge bRdyU ausgeloest
;
; Rechnet die Spannung am Spannungseingang
; aus und schreibt sie auf die LCD.
;
AusgabeU:
cbr rFlag,1<<bRdyU ; loesche Flagge
; mit Konstante cMultU 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 cMultI 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
Das ist ein solches Messbeispiel.
14.3.1 Hardware
Für die Messung der Temperatur sind keine externen Bauelemente erforderlich.
14.3.2 Messbereich, Mess-, Umrechnungs- und Darstellungsvorgang
14.3.2.1 Messbereich
ATMEL gibt für folgende Temperaturen typische ADC-Messergebnisse an:
t [°C] | ADC |
-40 | 230 |
25 | 300 |
85 | 370 |
14.3.2.2 Messvorgang
Die Temperaturmessung wird durch Umschaltung des Multiplexers auf Kanal 8 eingeschaltet. Der
entsprechende Umschaltbefehl ist im Handbuch angegeben und im Quelltext verwendet.
14.3.2.3 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.
14.3.2.4 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
;
; -------- Programmablauf --------------
;
; Misst nacheinander die Spannung
; a) am ADC3-Eingang (Widerstandsteiler),
; b) am Differenzeingang ADC2 und ADC0 mit
; interner Verstaerkung,
; c) am internen Temperatursensor
; fuer je 64 Male, summiert sie auf, rechnet
; sie in Spannung (a), Strom (b) und Tempe-
; ratur um und gibt sie auf der LCD aus.
;
; -------- 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 --
;
; ADC Ready Interrupt
; wird bei jeder abgeschlossenen Wandlung
; ausgeloest
;
; Liest das Messergebnis und summiert es auf.
; Zaehlt den Zaehler rCtr abwaerts. Wenn er
; Null erreicht, wird die Summe in einen
; Zwischenspeicher kopiert und der naechste
; Messkanal in die MUX geschrieben. Je nach-
; dem welcher Kanal zuletzt gemessen wurde,
; werden die Flaggen bRdyU, bRdyI und bRdyT
; gesetzt.
; In allen Faellen wird die naechste Wandlung
; gestartet.
;
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
;
; Routine AusgabeU
; wird von der Flagge bRdyU ausgeloest
;
; Rechnet das Messergebnis in eine Spannung um
; und gibt sie auf der LCD aus.
;
AusgabeU:
cbr rFlag,1<<bRdyU ; loesche Flagge
; mit Konstante cMultU 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
;
; Routine AusgabeI
; ausgeloest von der Flagge bRdyI
;
; Ergebnis in einen Strom umwandeln und auf
; der LCD ausgeben
;
AusgabeI:
cbr rFlag,1<<bRdyI ; loesche Flagge
; mit Konstante cMultI 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
;
; Routine AusgabeT
; wird von der Flagge bRdyT ausgeloest
;
; Rechnet das Ergebnis in eine Temperatur
; um und gibt sie auf der LCD aus
;
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
Das ist ein Beispiel für eine Temperaturmessung.
©2016-2018 by http://www.gsc-elektronic.net