;-----------------------------------------------------------------------------
;* Thermologger.asm            letzte Aenderung: 5.6.2009
;* Version: 1.3
;* Autor: Rolf Pfister
;* Copyright: Freeware
;* Prozessor: Atmega8, 8MHz
;* Schaltschema: thermologger.png
;*	 PortB: B0, B1, B2 mit LEDs verbunden, (B3 Basis auf NPN-Transistor)
;*       PortD:	D2, (D3) mit Taster verbunden, wenn gedrueckt == 0
;*	        D4, D5, D6, D7 fuer 3 1/2 stellige LCD-Anzeige (optional)
;*	 (Taster an D3 und Transistor an B3 fuer Thermostat benutzt)
;*   Variante 1: ADC0 (Pin 23) Thermowiderstand gegen GND, 2kOhm gegen AVCC
;*   Variante 2: 3.9kOhm gegen AVCC (noch nicht realisiert)
;*   Variante 3: ADC1 (Pin 24) (oder PD7(AIN1), dann aber LCD umhaengen)
;*	         Thermowiderstand und parallel 10uF gegen GND,
;*		 100 Ohm und Diode nach PC5.
;*
;* History:
;* 11.4.2009	Erstellung
;* 17.4.        Mit Packen der Daten (Version 0.1)
;* 25.4.        Mit Speichern im Flash (Version 0.2)
;* 27.4.   1.0  Speichern im Flash mit moeglichst kleinem Bootblock
;* 8.5.    1.1  Bei AD-Wandlung 8 Messungen aufsummieren u. Durchschnitt
;*              nehmen. (Durch dividieren mit 4 statt 8 koennte man noch ein
;*	        Bit mehr rausholen, dann aber Packer leicht aendern)
;* 25.5.   1.2  Option fuer LCD-Anzeige eingebaut
;* 26.5.	Erweiterung als Kontakt-Thermometer (Thermostat):
;*		PortD: PD3 zweiter Taster, PD3==0 --> aktuelle Temp. halten
;*		                        / -- Collector: Anschluss1
;*		PortB: PB3--4.7kOhm----|    NPN-Transistor BC550C
;*		                        \ -- Emitter: GND, Anschluss2
;*		Anschluss1 und Anschluss2 direkt mit entsprechenden
;*		Anschluessen am Oelbad (Heiz-Magentruehrer) verbinden.
;*		High an PB3 gibt Kontakt --> Heizung aus, Low --> Heizung ein
;* 3.6.    1.3  Versuch mit zweitem Thermofuehler
;*		Anschluss nach Variante3: an ADC1 gegen GND mit 10uF parallel,
;*		und 100 Ohm und Diode nach PC5 (zum Kondensator laden).
;*
;-----------------------------------------------------------------------------
.include "m8def.inc" 	; Definitionsdatei fuer den Prozessortyp
.equ SRAMSTART=0x60	;beim Atmega8
;.define MYAVR		;zum Austesten auf myAVR-Board mit 3.6864MHz
.equ	ANZAHLSENSOREN=2	;neu ab 1.3
.equ	SENSORTYP=222		;Temperatursensor KTY81-222
.equ	ANSCHLUSSVARIANTE=1	;Anschluss ueber 2kOhm, und AREF=AVCC
.equ	ANSCHLUSSVARIANTE2=3	;Anschluss des 2.Sensors
.equ	SENSORSPEICHERN=2	;1=1.Sensor, 2=Wert vom 2.Sensor speichern
;.equ	DATENFORMAT=0		;0=ungepackt = 2 Byte pro Messpunkt
.equ	DATENFORMAT=2		;1 oder 2=Einfaches packen = 0.5 bis 2 Byte pro Mess.
;.define EEPROMVERSION
.define FLASHVERSION
.define BOOTBLOCK2K
;.define BOOTBLOCK1K
.define LCDANZEIGE	;Anzeige der Temperatur auf einem LCD
			;Beschreibung des LCD ist in lcdanzeige.cc zu finden

;-----------------------------------------------------------------------------
;Datenformat:
; 1 Byte DATENFORMAT
; 1 Byte SENSORTYP
; 1 Byte ANSCHLUSSVARIANTE
; 1 Byte Messperiode = Anzahl Sekunden zwischen 2 Messungen
; 2 Byte N = Anzahl Messpunkte
;Wenn DATENFORMAT==0: N*2 Byte ungepackte Daten
;sonst je nach Packvariante weniger als N*2 Bytes
;
;DATENFORMAT==1:
; Wenn erstes Bit gesetzt, dann sind die naechsten 3 Bits
; die Differenz zum vorherigen Wert (Bereich -4 bis +3).
; Wenn das erste Bit nicht, aber das 2.Bit gesetzt ist,
; dann sind die naechsten 6 Bits +-4 die Differenz zum vorherigen Wert.
; (+-4 heisst: wenn Diff. positiv noch 4 addieren, wenn negativ 4 subtrahieren)
; Somit Wertebereich 4 bis 35 und -5 bis -36.
; Wenn weder das erste noch das 2. Bit gesetzt sind (c & 0xC0 == 0), dann
; sind die naechsten 10 Bits ein absoluter Wert.
; Also kurz zusammengefasst:
;       1X -> 4Bit, davon 3Bit Differenzwert (-4 ... +3)
;       01 -> 8Bit, davon 6Bit Differenzwert (-36 ... -5 und +4 ... +35)
;       00 -> 12Bit, davon 10Bit Absolutwert vorzeichenlos (0 ... 1023)
; Die Werte sind Messwerte von AD-Wandlung.
;
;DATENFORMAT==2:
; Wie 1 aber mit 14Bit-Absolutwerten:
;       00 -> 16Bit, davon 14Bit Absolutwert vorzeichenlos (0 ... 16383)
; Die Werte sind GradCelsius*10 (z.B. 357 bedeutet 35.7 Grad)
;
;-----------------------------------------------------------------------------
;Speicherbelegung:
;Global benutzt:
; r11:r10 Millisekunden, r12 Sekunden, r13 Minuten, r14 Stunden, r15 Tage
; r4=Flag fuer AD-Wandlung, neu (ab 1.1): Anzahl Aufsummierungen in AD-Wandler
; r5: Vorheriges Nibble beim gepackten Speichern (Gueltig=r5&0x10)
; r7:r6 letzter Messwert
; r8 = Zwischenspeicher fuer imflashspeichern
; r9 = Flag fuer erste Page fuer imflashspeichern
;      LCD-Erweiterung: r9 Bit0=Flag fuer erste Page, Bit1=OhneSpeichern-Flag
; r23:r22 Restlicher Platz in Byte
; r25:r24 Anzahl bisher gespeicherte Messpunkte
; r27:r26 == XH:XL = Zeiger auf zu speichernde Daten
; r29:r28 == YH:YL = Zeiger auf am Schluss zu speichernde Anzahl Messpunkte
; r31:r30 == ZH:ZL = Summe im AD-Interrupt, danach Zeiger zum umkopieren
; 
;im Hauptprogramm:
; r17:r16 fuer kurze Wartezeit (immer innerhalb 1...255)
; r16 auch als temporaere Variable
; r1=Messperiode Anzeige mit LEDs, 1=rot 2=gruen 4=gelb
; r2=Messperiode in Sekunden
; r3=1
; r19:r18 Millisekunden (eventuell falls noch Uhr benoetigt)
; r20,r21 Sollzeit
; 
;-----------------------------------------------------------------------------

.ifdef MYAVR
.equ QUARTZ=3686400	;zum Austesten mit myAVR-Board
.else
.equ QUARTZ=8000000	;Takt in MHz
.endif

.equ PAGESIZEB = PAGESIZE*2 ; PAGESIZEB is page size in BYTES, not words

.ifdef BOOTBLOCK2K
.org LARGEBOOTSTART
.endif
.ifdef BOOTBLOCK1K
.org THIRDBOOTSTART
.endif
; Reset und Interruptvectoren	;VNr. Beschreibung
begin:	rjmp	main		; 1   Power On Reset
	reti			; 2   Int0-Interrupt
	reti			; 3   Int1-Interrupt
	reti			; 4   TC2 Compare Match
	reti			; 5   TC2 Overflow
	reti			; 6   TC1 Capture
	reti			; 7   TC1 Compare Match A
	reti			; 8   TC1 Compare Match B
	reti			; 9   TC1 Overflow
	rjmp tc0over		; 10  TC0 Overflow
	reti			; 11  SPI, STC = Serial Transfer Complete
	reti			; 12  UART Rx Complete
	reti			; 13  UART Data Register Empty
	reti			; 14  UART Tx Complete
	rjmp adcomplete		; 15  ADC Conversion Complete
	reti			; 16  EEPROM ready
	reti			; 17  Analog Comperator
	reti			; 18  TWI (I2C) Serial Interface
	reti			; 19  Store Program Memory ready
;-----------------------------------------------------------------------------
; Start, Power On, Reset
main:	ldi r16, RAMEND&0xFF
        out SPL, r16  	     ; Init Stackpointer L
	ldi r16, RAMEND>>8
        out SPH, r16  	     ; Init Stackpointer H

;; Init-Code
	ldi r16, 0xFF		;Alle Leitungen auf Ausgang
        out DDRB, r16		;Data Direction Register B setzen
	ldi r16, 0
	out PORTB, r16		;alle LEDs loeschen
        ldi r17, 0xF0
        out DDRD, r17		;4 Obere Leitungen Port D auf Ausgang 4 untere auf Eingang
        ldi r17, 0b00001100	;D2,D3 Pullupwiederstaende setzen
        out PORTD, r17	        ;PortD Pullupwiederstaende setzen
.if ANZAHLSENSOREN>1
	ldi r16, 0x20		;PC5 als Ausgang
	out DDRC, r16
;	out PORTC, r16		;PC5 setzen
.endif

	ldi r17, 0x01
	out TIMSK, r17		;Timer/Counter0 Overflow einschalten
	ldi r17, 3		;1=nicht teilen, 2=durch 8, 3=64, 4=256, 5=1024
	out TCCR0, r17		;Vorteiler auf 64 setzen
;Bei 8MHz wird somit in 8us Schritten gezaehlt (bei 3.6864MHz etwa 8*2.17us)
;Um alle 1000usec einen Interrupt zu erhalten muss der Zaehler jeweils bei
;256-125=131 gestartet werden.
;(bei 3.6864MHz 256-57.6=198.4 oder jeweils 2*57 und 3*58 us)
.ifdef MYAVR
.equ	ZAEHLER =198		; 131 bei 8MHz, 198 oder 199 bei 3.6864MHz
.else
.equ	ZAEHLER =131		; 131 bei 8MHz, 198 oder 199 bei 3.6864MHz
.endif
	ldi r17, ZAEHLER
	out TCNT0, r17
.ifdef FLASHVERSION
	ldi r17, (1<<IVCE)	;Veraendern von IVSEL vorankuendigen
	out GICR, r17
	ldi r17, (1<<IVSEL)	;Interrupttabelle auch bei Bootadresse
	out GICR, r17
.endif
	clr r9
	clr r10
	clr r11			; r11r10 = Millisec
	clr r12			; Sekunden
	clr r13			; Minuten
	clr r14			; Stunden
	clr r15			; Tage, auf 0 setzen
	sei			;Interrupt erlauben

; AD-Wandler initialisieren:
	clr r4			;Speichern-Flag fuer AD-Interrupt-Routine
	clr r30
	clr r31			;Summe = 0
;zum Austesten mit SRAM:
;	ldi XH, 0
;	ldi XL, 0x60		;test: X auf Anfang des SRAM setzen
	ldi XH, 0
	ldi XL, 0		;X auf Anfang im EEPROM oder FLASH setzen
	ldi r16, 0x40		;ADC0 und Referenz=AVCC (mit externem Condenser)
;Anschlussvariante2:
;	ldi r16, 0xC0		;ADC0 und Referenz=2.56V (ev.externer Condenser)
	out ADMUX,r16
;Prescaler: 5=32 6=64 7=128, 8MHz/64=125kHz (muss im Bereich 50-200 liegen)
;AD-Wandlung braucht jeweils 13 Takte also 104usec
.ifdef MYAVR
.equ	PRESCALER=5
.else
.equ	PRESCALER=6
.endif
;r16 = Prescaler+0b11001000 (ADEN+ADSC+ADIE = Enable,Einzelstart,Interrupt)
;ADIF (0x10) wird automatisch vom AD-Wandler gesetzt wenn Wandlung fertig.
	ldi r16, 0b11001000 + PRESCALER
	out ADCSRA, r16		;Prescaler und Enable

	clr r1			;Start-Parameter
	inc r1			;Start mit Roter LED
	mov r3, r1		;r3=1

.ifdef EEPROMVERSION
	.equ MAX=512		;Atmega8 hat 512 Bytes = 256 WORDs EEPROM
.endif
.ifdef BOOTBLOCK2K
	.equ MAX=LARGEBOOTSTART*2
.endif
.ifdef BOOTBLOCK1K
	.equ MAX=THIRDBOOTSTART*2
.endif
.if DATENFORMAT==0
	ldi r22, low(MAX-6)	;Freier Speicher minus 6 Bytes Kopfdaten 
	ldi r23, high(MAX-6)
.else ;if DATENFORMAT<=2
	ldi r22, low(MAX-8) ;bei gepacktem Speichern noch 2 Reserve
			;zum am Schluss noch ev. letztes Nibble zu speichern
			;(2 weil manchmal gleich 2 Byte gespeichert werden)
	ldi r23, high(MAX-8)
.endif
	clr r17			;r17=0
	ldi r18, SENSORSPEICHERN
	clr r5			;Nibble fuer gepacktes Speichern loeschen
	rcall thermostat_init
;-----------------------------------------------------------------------------
.if ANZAHLSENSOREN>1
vorstartloop:		;Widerstand des 2.Sensors anzeigen
	rcall messungohnespeichern
	rcall widerstandauflcdanzeigen
	sbic PIND, 2		;Skip if Bit Clear, Taster gedrueckt?
	rjmp vorstartloop	;nein--> warten
	ldi r16, 200
	rcall milliwait
.endif
startloop:
	rcall ledportb		;ausgewaehlte LED leuchten lassen
.ifdef LCDANZEIGE
	rcall messungohnespeichern
	rcall tempauflcdanzeigen
	ldi r16, 5
	rcall milliwait
	rcall thermostat
.endif
	sbic PIND, 2		;Skip if Bit Clear, Taster gedrueckt?
	rjmp startloop		;nein--> warten
	ldi r16, 200		;r17:r16=200
	rcall milliwait		;200ms warten
	sbis PIND, 2		;Skip if Bit Set, Taster losgelassen?
	rjmp evlangertastendruck	;nein--> langer Tastendruck
kurzertastendruck:
	lsl r1			;naechste LED
	sbrc r1,3		;0x08 erreicht ?
	mov r1,r3		;ja: r1=1
	rjmp startloop
evlangertastendruck:	
	ldi r16, 250
	rcall milliwait		;250ms warten
	sbic PIND, 2		;Skip if Bit Clear, Taster noch gedrueckt?
	rjmp kurzertastendruck	;nein-> doch nur kurzer Tastendruck
langertastendruck:
	rcall ledportbclear
	ldi r16, 250
	rcall milliwait		;250ms warten
	rcall ledportb
	rcall milliwait		;250ms warten
	sbis PIND, 2		;Skip if Bit Set, Taster losgelassen?
	rjmp langertastendruck	;nein--> warten
aufganzesekundewarten:
;	cli			;Interrupt verbieten
;	mov r18, r10
;	mov r19, r11		;r19:r18 = Startzeit Millisekunden
;	sei			;Interrupt erlauben
;	ldi r16, 0xE8
;	ldi r17, 0x03		;r17:r16 = 1000
;	sub r16, r18
;	sbc r17, r19		;r17:r16 -= aktuelle Millisekunden
;	rcall milliwait		;auf ganze Sekunde warten
;	clr r17
	clr r10			;Millisekunden auf 0 setzen
	clr r11			;ok solange wir keine Echtzeit-Uhr brauchen
	mov r20, r12		;r20 = Startzeit Sekunden
	mov r21, r13		;r21 = Startzeit Minuten

; r1 --> r2 = Messperiode in Anzahl Sekunden:
	sbrc r1, 0		;Skip if Bit in Register Clear, Rot gewaehlt?
	rjmp auswahl1		;ja-->
	sbrc r1, 1		;Skip if Bit in Register Clear, Gruen gewaehlt?
	rjmp auswahl2		;ja-->
auswahl3:
	ldi r16, 60		;60 Sekunden Messperiode
	rjmp auswahlr2
auswahl1:
	ldi r16, 1		;1 Sekunde Messperiode
	rjmp auswahlr2
auswahl2:
	ldi r16, 10		;10 Sekunden Messperiode
auswahlr2:
	mov r2, r16
	
;Datenkopf schreiben:
	ldi r16, DATENFORMAT
	rcall bytespeichern
	ldi r16, SENSORTYP
	rcall bytespeichern
.if ANZAHLSENSOREN>1
	ldi r16, ANSCHLUSSVARIANTE2
.else
	ldi r16, ANSCHLUSSVARIANTE
.endif
	rcall bytespeichern
	mov r16, r2
	rcall bytespeichern	;Messperiode in Sekunden
	clr r24
	clr r25			;Anzahl Messpunkte = 0
	mov YH,XH		;Y = Zeiger zum am Schluss Anzahl
	mov YL,XL		;Messpunkte zu speichern.
	ldi r16, 0xFF
	rcall bytespeichern	;Anzahl Messpunkte erst mal auf ungueltigen
	rcall bytespeichern	;Wert (0xFFFF) setzen.
;	clr r6			;Letzter Messwert auf 0 setzen nicht noetig
;	clr r7			;weil erster Messwert eh Absolutwert.
messloop:
	rcall messung		;AD-Wandlung machen
	add r20, r2		;neue Sollzeit = alte Sollzeit + Messperiode
	cpi r20, 60		;r20<60 ?
	brlt warteloop		;ja-->
	subi r20, 60		;nein: 60 subtrahieren
	;braucht da noch Anpassung falls Messperiode groesser 60 sein kann!
	inc r21			;und Minuten erhoehen
	cpi r21, 60
	brne warteloop
	clr r21
warteloop:
.ifdef LCDANZEIGE
	rcall tempauflcdanzeigen
	rcall thermostat
.endif
	sbis PIND, 2		;Skip if Bit Set, Taster gedrueckt?
	rjmp evvorzeitig	;ja--> ev. vorzeitig beenden
	cp r20, r12		;Sollzeit Sekunden erreicht?
	brne warteloop
	cp r21, r13		;Sollzeit Minuten erreicht?
	brne warteloop
	tst r23			;Speicher voll: restlicher Platz == 0?
	brne messloop		;nein-->
	tst r22
	brne messloop		;nein-->
	rjmp mfertig
evvorzeitig:
	ldi r16, 255
	rcall milliwait		;255ms warten
	sbis PIND, 2		;Skip if Bit Set, Taster immer noch gedrueckt?
	rcall milliwait		;ja: nochmals 255ms warten
	sbic PIND, 2		;Taster immer noch gedrueckt?
	rjmp warteloop		;nein--> dochnicht
mfertig:tst r4
	brne mfertig		;allenfalls warten bis laufende Messung fertig
	sbrs r5, 4		;Bit 4 in r5 gesetzt?
	rjmp mfert2		;nein --> kein vorheriges Nibble
	mov r16, r5
	swap r16
	andi r16, 0xF0		;nicht unbedingt noetig (unteres Nibble wird
				;eh verworfen), aber leichter zum debuggen.
	rcall bytespeichern	;letztes Nibble auch noch speichern
mfert2:
.ifdef EEPROMVERSION
	mov XH, YH
	mov XL, YL
	mov r16, r25
	rcall eepromstore
	mov r16, r24
	rcall eepromstore	;Anzahl Messpunkte speichern
.else
	ldi r16, 0xFF
	rcall endeimflashspeichern	;Rest der Page mit 0xFF fuellen
;	rcall page1insramkopieren   geht so nicht!
; Erste Page wurde schon in imflashspeichern ins SRAM kopiert
	ldi XL, low(SRAMSTART)
	ldi XH, high(SRAMSTART)
	add XL, YL
	adc XH, YH
	st X+, r25		;Anzahl Messpunkte im SRAM
	st X+, r24		;noch richtig eintragen.
	rcall page1zurueckkopieren
.endif
halteloop:
.ifdef LCDANZEIGE
	rcall messungohnespeichern
	rcall tempauflcdanzeigen
	rcall thermostat
.endif
	lsl r1
	sbrc r1, 3		;0x08 erreicht ?
	mov r1, r3		;ja: r1=1
	rcall ledportb
	ldi r16, 255
	rcall milliwait		;255ms warten
	rjmp halteloop
ledportb:
	push r16
	in r16, PORTB
	andi r16, 0xF8		;obere Bits unveraendert lassen
	or r16, r1		;untere 3 Bits = ausgewaehlte LED
	rjmp ledp1
ledportbclear:
	push r16
	in r16, PORTB
	andi r16, 0xF8		;obere Bits unveraendert lassen
ledp1:	out PORTB, r16
	pop r16
	ret
	
.ifdef FLASHVERSION

.else
bytespeichern:
eepromstore:
;	st X+, r16	;fuer test mit SRAM
;	ret		;fuer test mit SRAM
eeL1:	sbic EECR,1
	rjmp eeL1	;warte bis EEPROM bereit
	out EEARH,XH
	out EEARL,XL
	out EEDR, r16
	sbi EECR, 2	;EEMWE-Bit setzen
	sbi EECR, 1	;EEWE-Bit setzen
	adiw XL, 1
	ret
.endif

page1zurueckkopieren:		;Page vom SRAM zurueck ins FLASH kopieren
	ldi XL, 0
	ldi XH, 0
	ldi ZL, low(SRAMSTART)
	ldi ZH, high(SRAMSTART)
	ldi r17, PAGESIZEB
kopL2:	ld r16, Z+
	rcall imflashspeichern
	dec r17
	brne kopL2
	ret

;-----------------------------------------------------------------------------
; Interrupt-Routine
; Registerbelegungen:
; r11:r10 Millisekunden, r12 Sekunden, r13 Minuten, r14 Stunden, r15 Tage
tc0over:push r16
	push r17
	in r17, SREG	;Statusregister sichern
	push r17
	ldi r17, ZAEHLER
	out TCNT0, r17
	ldi r16, 0xE8
	ldi r17, 0x03	;r17:r16 = 1000 = 0x03E8
	inc r10		;Millisekunden erhoehen
	brne L1
	inc r11
L1:	cp r10, r16
	brne end1	;fertig wenn msec!=1000
	cp r11, r17
	brne end1	;fertig wenn msec!=1000
	clr r10
	clr r11		;Millisekunden auf 0 setzen
	inc r12		;Sekunden erhoehen
	ldi r16, 60
	cp r12, r16
	brne end1	;fertig wenn sec!=60
	clr r12		;Sekunden auf 0 setzen
	inc r13		;Minuten erhoehen
	cp r13, r16
	brne end1	;fertig wenn min!=60
	clr r13		;Minuten auf 0 setzen
	inc r14		;Stunden erhoehen
	ldi r16, 24
	cp r14, r16
	brne end1	;fertig wenn std!=24
	clr r14		;Stunden auf 0 setzen
	inc r15		;Tage erhoehen
end1:	pop r17
	out SREG, r17	;Statusregister zurueckholen
	pop r17
	pop r16
	reti

;-----------------------------------------------------------------------------
;Interrupt-Routine fuer AD-Wandlung
; Registerbelegungen:
; r27:r26 == XH:XL = Zeiger auf zu speichernde Daten
; r4 = Anzahl Aufsummierungen oder 0 um erste Wandlung zu verwerfen,
;      auf 0 gesetzt wenn AD-Wandlung fertig
; r9 Bit1 = OhneSpeichern-Flag (fuer LCD)
; r25:r24 Anzahl gespeicherte Messpunkte += 1
adcomplete:
	push r16
	push r17
	in r17, SREG	;Statusregister sichern
	push r17
	tst r4		;allenfalls erste Wandlung verwerfen
	breq _end2
	in r17, ADCL	;L muss zuerst gelesen werden!
	in r16, ADCH
	add r30, r17
	adc r31, r16	;Summe += Messwert
	dec r4		;letzter Messert?
	brne _end3	;nein-->
	inc r4		; ja: r4 wieder richtig bevor sei..
	sei		;Interrupts erlauben, noetig weil EEPROM eventuell
			;laenger braucht (und Uhr weiter laufen muss)
	;(ok EEPROM braucht nicht soo lange, aber beim FLASH wirklich noetig)
	asr r31		; ..und Durchschnitt berechnen.
	ror r30
	asr r31
	ror r30
	asr r31
	ror r30			;Summe /= 8
.ifdef LCDANZEIGE
	sbrs r9, 1		;OhneSpeichern-Flag gesetzt?
	rjmp _L1		;nein-->
.if ANZAHLSENSOREN==1
	mov r6, r30
	mov r7, r31		;letzter Messwert setzen
.endif
	rjmp _end1
.endif
_L1:	mov r17, r30
	mov r16, r31
.if DATENFORMAT==0
	rcall bytespeichern2 	;H zuerst speichern
	mov r16, r17
	rcall bytespeichern2	;L danach speichern
.else ;if DATENFORMAT<=2
	rcall gepacktspeichern
.endif
;	adiw r24, 1	;r25:r24 += 1, wird jetzt in "messung:" gemacht
;	out PORTB, r22		;test
_end1:	dec r4
;	brne _end3  ;weiter oben schon gemacht
_end2:	clr r30
	clr r31
	rjmp _end4
_end3:	sbi ADCSRA,6		;Starte naechste AD-Wandlung
_end4:	pop r17
	out SREG, r17	;Statusregister zurueckholen
	pop r17
	pop r16
	reti

;-----------------------------------------------------------------------------
; Subroutine gepacktspeichern(UWORD r16:r17)
; r25:r24 = Anzahl bisherige Messpunkte, unveraendert
; r5 = vorheriges Nibble falls Bit 4 gesetzt, wird aktualisiert
; r7:r6 = alter Messwert, wird dann aktualisiert
; r16:r17 = neuer Messwert, geht verloren, ja wirklich r16=H r17=L
gepacktspeichern:
	push r1
	push r2
	mov r1, r16
	mov r2, r17		;Aktueller Messwert: r1:r2 = r16:r17
	tst r24			;Anzahl bisherige Messpunkte == 0 ?
	brne packstart		;nein -->
	tst r25
	breq pack0		;ja --> ungepackter Absolutwert
packstart:
;       1X -> 4Bit, davon 3Bit Differenzwert (-4 ... +3)
;       01 -> 8Bit, davon 6Bit Differenzwert (-36 ... -5 und +4 ... +35)
;       00 -> 12Bit, davon 10Bit Absolutwert (0 ... 1023)
;mit DATENFORMAT==2:
;       00 -> 16Bit, davon 14Bit Absolutwert (0 ... 16383)
	sub r17, r6	;r7:r6 = alter Wert, (r1:r2 = neuer Wert)
	sbc r16, r7	;r16:r17 = Differenz
	tst r16
	breq pack9	;positive Differenz weniger als 9 Bit -->
	cpi r16, 0xFF	;negative Differenz weniger als 9 Bit ?
	brne pack0	;nein --> ungepackter Absolutwert
pack9:	sbrs r17, 7	;Differenz negativ?
	rjmp pack9p	;nein -->
	cpi r17, 0xFC	;passt negativer Wert in 3 Bit?
	brcc pack1	;ja --> in 4 Bit = 1Nibble packen
	subi r17, -4	;Diff.Wertebereich erhoehen
	cpi r17, 0xE0	;passt negativer Wert in 6 Bit?
	brcs pack0	;nein --> Absolutwert speichern
	rjmp pack2	;ja --> in 2 Nibbles packen
pack9p:	cpi r17, 0x04	;passt positiver Wert in 3 Bit?
	brcs pack1	;ja --> in 1Nibble packen
	subi r17, 4	;Diff.Wertebereich erhoehen
	cpi r17, 0x20	;passt positiver Wert in 6 Bit?
	brcc pack0	;nein --> Absolutwert speichern
pack2:			;in 8 Bit = 2Nibble packen
	andi r17,0x3F	;6 Nutzbits
	ori r17, 0x40	;01 als erste 2 Bits
	sbrs r5, 4	;Bit 4 in r5 gesetzt?
	rjmp pack2k	;nein --> kein vorheriges Nibble
	swap r5		;vorheriges Nibble zuerst..
	mov r16, r5
	andi r16, 0xF0	 ;und zusammen mit..
	push r17
	swap r17
	andi r17, 0x0F 	  ;erstem Nibble..
	add r16, r17
	pop r17
	rcall bytespeichern2  ;abspeichern.
	andi r17,0x0F		;zweites Nibble
	ori r17, 0x10		;als gueltig markieren
	mov r5, r17		;und nach r5 kopieren.
	rjmp packret
pack2k:	mov r16, r17		;genau 1 Byte speichern
	rcall bytespeichern2
	rjmp packret
pack1:	;in 4 Bit packen
	andi r17,0x07
	ori r17, 0x08
	sbrs r5, 4		;Bit 4 in r5 gesetzt?
	rjmp pack1k		;nein --> kein vorheriges Nibble
	swap r5			;ja: vorheriges Nibble einfuegen
	mov r16, r5
	andi r16, 0xF0
	add r16, r17
	rcall bytespeichern2
	clr r5			;r5 als ungueltig markieren
	rjmp packret
pack1k:	ori r17, 0x10		;als gueltig markieren
	mov r5, r17		;und in r5 kopieren
packret:mov r7, r1
	mov r6, r2		;letzter Messwert r7:r6 = r1:r2 = neuer Wert
	rjmp packret2
.if DATENFORMAT==2
pack0:				;16Bit Absolutwert (=r1:r2) speichern
	mov r7, r1
	mov r6, r2		;letzter Messwert r7:r6 = r1:r2 = neuer Wert
	sbrs r5, 4		;Bit 4 in r5 gesetzt?
	rjmp pack04		;nein --> kein vorheriges Nibble
	ldi r17, 0x18		;ja: schiebe Nibble r5 -> r1:r2 -> r5
paL2:	lsr r5
	ror r1
	ror r2
	ror r17
	brcc paL2
	swap r17
	mov r5, r17		;uebriges Nibble
pack04:	mov r16, r1
	rcall bytespeichern2 	;vorheriges und oberstes Nibble speichern
	mov r16, r2
	rcall bytespeichern2	;2 mittlere Nibbles speichern
.else
pack0:				;12Bit Absolutwert (=r1:r2) speichern
	sbrs r5, 4		;Bit 4 in r5 gesetzt?
	rjmp pack03		;nein --> kein vorheriges Nibble
	;andi r1, 0x03	;nur noetig wenn obere Bits in r1 nicht schon 0 waeren.
	mov r16, r5		;ja: Nibble r5 -> oberes Nibble r16:r17
	swap r16		;r16 = r5<<4
	andi r16, 0xF0
	add r16, r1
	rcall bytespeichern2 	;2 obere Nibbles zuerst speichern
	mov r16, r2
	rcall bytespeichern2	;2 untere Nibbles danach speichern
	rjmp packret
pack03:				;3 Nibbles speichern
	mov r7, r1
	mov r6, r2		;letzter Messwert r7:r6 = r1:r2 = neuer Wert
	ldi r16, 4
paL1:	lsl r2
	rol r1
	dec r16
	brne paL1		;r1:r2 um 4 Bit nach links schieben
	mov r16, r1
	rcall bytespeichern2	;erste 2 Nibbles wirklich speichern
	mov r5, r2
	swap r5			;drittes Nibble im r5
	set			;T-Flag setzen
	bld r5, 4		;in r5 Bit4=T == r5 als gueltig markieren
.endif
packret2:
	pop r2
	pop r1
	ret

;-----------------------------------------------------------------------------
milliwait1:			;Wartezeit: r16, 1 bis 255 msec
	push r17
	clr r17
	rjmp miL1
milliwait:			;Wartezeit: r17:r16 msec, min 1 max 1000
	push r17
miL1:	push r16
	push r18
	mov r18, r10
loop1:	cp  r18, r10
	breq loop1		;1. Millisekunde abwarten (0 bis 1 ms)
	add r16, r10
	adc r17, r11		;sollzeit = Wartezeit + aktuelle Millisek
	subi r16, low(1001)
	sbci r17, high(1001)	;sollzeit -= 1; sollzeit -= 1000;
	brpl loop2		;wenn sollzeit>=0 -->
	subi r16, low(-1000)	;sonst haben wir 1000 zuviel subtrahiert
	sbci r17, high(-1000)	;also wieder 1000 addieren (soll -= -1000)
loop2:	cp r16, r10
	cpc r17, r11		;auf sollzeit warten
	brne loop2
	cp r16, r10		;falls zwischen 1. u 2. cp ein Interrupt war
	brne loop2
	pop r18
	pop r16
	pop r17
	ret

;-----------------------------------------------------------------------------
; Messung machen und Messpunkt speichern
messung:
	push r16
	push r18
.ifdef LCDANZEIGE
.if SENSORSPEICHERN==1
	ldi r16, 0xFD
	and r9, r16		;r9 Bit1 loeschen
.else
	ldi r16, 2
	or r9, r16		;Bit1 in r9 setzen: OhneSpeichern-Flag
.endif
.endif
mess1:	tst r4
	brne mess1		;ev. warten bis vorherige AD-Wandlung fertig
	ldi r18, 8		;Anzahl AD-Aufsummierungen
	mov r4, r18		;fuer naechste Wandlung setzen.
	sbi ADCSRA,6		;Starte naechste AD-Wandlung
	ldi r18, 3		;LED 3 mal blinken
blinkloop1:
	ldi r16, 100
	rcall milliwait		;100ms warten
	rcall ledportb
	rcall milliwait		;100ms warten
	rcall ledportbclear
	dec r18
	brne blinkloop1
.if ANZAHLSENSOREN>1
	ldi r16, 2
	eor r9, r16		;OhneSpeichern-Flag umkehren
	rcall messung2
.endif
	adiw r24, 1		;r25:r24 += 1, Anzahl gespeicherte Messpunkte
	pop r18
	pop r16
	ret

.if ANZAHLSENSOREN>1
.equ widerstandL=SRAMSTART+PAGESIZEB+5
.equ widerstandH=SRAMSTART+PAGESIZEB+6
.equ sensor2gradL=SRAMSTART+PAGESIZEB+7
.equ sensor2gradH=SRAMSTART+PAGESIZEB+8
messung2:		;Messung 2.Sensor, r18 zerstoert
	push r0
	push r1
	push r2
	push r3
	push r17
mess2:	tst r4
	brne mess2		;ev. warten bis AD-Wandlung fertig
	cbi ADCSRA, ADEN	;AD-Wandler aus
	in r16, SFIOR
	ori r16, (1<<ACME)	;Comperator Multiplexer ein
	out SFIOR, r16
	sbi ACSR, ACBG		;Interne Referenz fuer Comperator
	ldi r16, 0xC1		;Interne Referenzspannung ein und Mux=1
	out ADMUX,r16
	ldi r18, 0x20
	out PORTC, r18		;PC5 setzen, C fuer die Messung laden
	ldi r16, 100
	rcall milliwait1	;kurz warten
	clr r16
	clr r17
	clr r18
	out PORTC, r18		;PC5 aus;
mess3:	subi r16, 0xFF		; r18:r17:r16 += 1
	sbci r17, 0xFF
	sbci r18, 0xFF
	cpi r18, 0x02		;Abbruch bei Ueberlauf
	brcc mess4
	sbis ACSR, ACO		; ACO-Bit gesetzt ?
	rjmp mess3		; nein-->
mess4:
	;; r18:r17:r16 ist jetzt Entladezeit in Takt*8
	;; bei 8MHz also gerade Anzahl Microsec
	mov r3, r18
	mov r2, r17
	mov r1, r16
	clr r0			;mit 256 Multipliziert um genaueres K2 zu haben
.equ konst2=3036		;Mit Eichwiderstand ausmessen
	ldi r16, low(konst2)
	ldi r17, high(konst2)	; R=K2/t2
	rcall div16		;r3:r2:r1:r0 /= r17:r16, r17:r16 = Rest
	sts widerstandL, r0
	sts widerstandH, r1
	in r16, SFIOR
	andi r16, ~ACME		;Comperator Multiplexer aus
	out SFIOR, r16
	sbi ADCSRA, ADEN	;AD-Wandler ein
	ldi r16, 0x40		;AVcc als Referenzspannung und Mux=0
	out ADMUX,r16
.if SENSORSPEICHERN==2
	ldi r16, 3
	rcall umrechnen		;umrechnen in 10*GradCelsius
	sts sensor2gradL, r0
	sts sensor2gradH, r1
	mov r17, r0
	mov r16, r1
	sbrs r9, 1		;OhneSpeichern-Flag gesetzt?
	rcall gepacktspeichern	;nein: speichern
.endif
	pop r17
	pop r3
	pop r2
	pop r1
	pop r0
	ret

widerstandauflcdanzeigen:
	push r17
	push r18
	push r19
	clr r18
	lds r16, widerstandL
	lds r17, widerstandH
	rcall zahlauflcd
	pop r19
	pop r18
	pop r17
	ret
.endif
.ifdef LCDANZEIGE
messungohnespeichern:
	push r18
messo1:	tst r4
	brne messo1		;ev. warten bis vorherige AD-Wandlung fertig
	ldi r18, 8		;Anzahl AD-Aufsummierungen
	mov r4, r18		;fuer naechste Wandlung setzen.
	ldi r18, 2
	or r9, r18		;Bit1 in r9 setzen: OhneSpeichern-Flag
	sbi ADCSRA,6		;Starte naechste AD-Wandlung
	rcall messung2
	pop r18
	ret
.endif

;-----------------------------------------------------------------------------
; Routinen zum Speichern im Flash

bytespeichern2:		;Byte speichern und den Zaehler r23:r22 erniedrigen
	subi r22, 1
	sbci r23, 0
			;Speicher voll? (sollte eigentlich nicht passieren)
	brcs return	;ja --> nichts machen
bytespeichern:
imflashspeichern:		;1 Byte im Flash speichern
	;; zuerst Zwischenspeichern und dann 2 Byte aufs mal speichern
	;; jeweils Page loeschen bevor das erste Word gespeichert wird.
	;; r16 = zu speicherndes Byte
	;; XH:XL = Zeiger im Flash, wird um 1 erhoeht
	.def zwischenspeicher=r8
; oder falls kein Register belegt werden soll:
;	.equ ZWISCHENSPEICHER=SRAMSTART+256 ;2Byte im SRAM
	push r0
	push r1
	push r16
	push r17
	push ZL
	push ZH
	sbrc r9, 0		;schreiben wir in erste Page?
	rjmp imfL1		;nein-->
	ldi ZL, low(SRAMSTART)	;ja: auch noch ins SRAM kopieren
	ldi ZH, high(SRAMSTART)
	add ZL, XL
	adc ZH, XH
	st Z+, r16
	cpi ZL, SRAMSTART+PAGESIZEB ;erste Page voll?
	brcs imfL1		    ;nein-->
	inc r9			    ;ja: r9=1 (bzw Bit0 in r9 auf 1 setzen)
imfL1:	mov ZL, XL
	mov ZH, XH
	sbrs ZL, 0		;Z ungerade?
	rjmp zwischenspeichern	;nein--> erstes Byte zwischenspeichern
wordspeichern:
	dec ZL			;immer auf gerade Adresse 2 Bytes speichern
	mov r0, zwischenspeicher ;oder lds r0, ZWISCHENSPEICHER
	mov r1, r16
	ldi r17, (1<<SPMEN)
	rcall do_spm17
	adiw ZH:ZL, 2
	mov r17, ZL
	andi r17, PAGESIZEB-1	;wird beim naechsten Mal neue Page erreicht?
	brne incxandreturn	;nein-->
	subi ZL, low(PAGESIZEB)	;ja:  execute page write
	sbci ZH, high(PAGESIZEB) ;restore pointer
	ldi r17, (1<<PGWRT) + (1<<SPMEN)
	rcall do_spm17
	rjmp incxandreturn
zwischenspeichern:
	mov zwischenspeicher, r16	;oder sts ZWISCHENSPEICHER, r16
	mov r17, ZL
	andi r17, PAGESIZEB-1	;neue Page?
	brne incxandreturn	;nein-->
	ldi r17, (1<<PGERS) + (1<<SPMEN) ;ja: neue Page zuerst loeschen
	rcall do_spm17
incxandreturn:
	adiw XH:XL, 1
	pop ZH
	pop ZL
	pop r17
	pop r16
	pop r1
	pop r0
return:	ret
do_spm17:	;r17 ins SPMCR speichern und spm durchfuehren, r16 zerstoert
	in r16, SREG
	push r16	;; disable interrupts if enabled, store status
	cli
spmwait: in r16, SPMCR	;; check for previous SPM complete
	sbrc r16, SPMEN
	rjmp spmwait
	out SPMCR, r17	;; SPM timed sequence
	spm
	pop r16 ;; restore SREG (to enable interrupts if originally enabled)
	out SREG, r16
	ret
endeimflashspeichern:		;Rest der Page mit r16 auffuellen
	push r17
eflashloop:
	mov r17, XL
	andi r17, PAGESIZEB-1	;neue Page erreicht?
	breq eflashfertig	;ja-->
	rcall imflashspeichern
	rjmp eflashloop
eflashfertig:
	pop r17
	ret

.ifdef LCDANZEIGE
;-----------------------------------------------------------------------------
;; LCD-Anzeige

; Variablen im SRAM:
.equ polari=SRAMSTART+PAGESIZEB		;LCD-Polaritaet
.equ sollwertL=SRAMSTART+PAGESIZEB+1
.equ sollwertH=SRAMSTART+PAGESIZEB+2	;Sollwert fuer Thermostat
.equ istwertL=SRAMSTART+PAGESIZEB+3
.equ istwertH=SRAMSTART+PAGESIZEB+4	;Istwert fuer Thermostat
	
tempauflcdanzeigen:
	push r0
	push r1
	push r16
	push r17
	push r18
	push r19
adwait:	tst r4
	brne adwait		;ev. warten bis laufende Messung fertig
	mov r0, r6		;r23:r22 (in anderm Testprog) oder r7:r6 (hier) ist aktueller Messwert
	mov r1, r7		; r1:r0 = AD-Messwert
	ldi r16, 1
	rcall umrechnen
	mov r16, r0
	mov r17, r1		; r17:r16 = GradCelsius*10
	sts istwertL, r16
	sts istwertH, r17
	cpi r18, 2		;2.Sensor anzeigen?
	brne temp1		;nein-->
	lds r16, sensor2gradL
	lds r17, sensor2gradH
temp1:	ldi r18, 1		; Dezimalpunkt vor letzter Stelle
	rcall zahlauflcd
	pop r19
	pop r18
	pop r17
	pop r16
	pop r1
	pop r0
	ret

;-----------------------------------------------------------------------------
;void datenschieben(ulong n)
datenschieben:		;r16:r17:r18:r19 = zu schiebende 32 Bit
	push r20
	push r21
	ldi r20, 32	;for(int i=0;i<32;i++,n<<=1)
datL1:	in r21, PORTD
	andi r21, 0x0F	;untere 4 Bits unveraendert lassen
	ori r21, 0x20	;0x20=OE (vorhergehende Daten immer noch anzeigen)
	out PORTD, r21
	sbrc r16, 7		;if(n&0x80000000)
	ori r21, 0x40		;  c |= 0x40; //zu schiebendes Datenbit
	out PORTD, r21
	ori r21, 0x10		;c |= 0x10;//CLK
	out PORTD, r21		;PORTD = c;
	lsl r19
	rol r18
	rol r17
	rol r16			;n<<=1
	dec r20
	brne datL1
	ori r21, 0x80
	out PORTD, r21		;PORTD = c |= 0x80;//STR
	in r21, PORTD
	andi r21, 0x0F	;untere 4 Bits unveraendert lassen
	ori r21, 0x20
	out PORTD, r21		;PORTD=0x20;//OE
	pop r21
	pop r20
	ret

.equ SEG3DP=1
.equ SEG3E=2
.equ SEG3D=4
.equ SEG3C=8
.equ SEG2DP=0x10
.equ SEG2E=0x20
.equ SEG2D=0x40
.equ SEG2C=0x80
.equ SEG1DP=0x100
.equ SEG1E=0x200
.equ SEG1D=0x400
.equ SEG1C=0x800
.equ SEG1B=0x1000
.equ SEG1A=0x2000
.equ SEG1F=0x4000
.equ SEG1G=0x8000
.equ SEG2B=0x10000
.equ SEG2A=0x20000
.equ SEG2F=0x40000
.equ SEG2G=0x80000
.equ SEGPin28=0x100000
.equ SEG3B=0x200000
.equ SEG3A=0x400000
.equ SEG3F=0x800000
.equ SEG3G=0x1000000
.equ SEGPin38=0x2000000
.equ SEGPin39=0x4000000
.equ SEGPin3=0x8000000
.equ SEGPin2=0x10000000
.equ SEGCOM=0x20000000

.equ SEG4BC=SEGPin3		;//kann bei andern LCDs anders sein
.equ SEGMinus=SEGPin39		;//kann bei andern LCDs anders sein
.equ SEGBatt=SEGPin2		;//kann bei andern LCDs anders sein
.equ SEGOver=SEGPin38		;//kann bei andern LCDs anders sein
;.equ SEGDoppelpunkt=SEGPin28	;//bei diesem LCD nicht vorhanden
;static ulong siebensegdec1[10]={
siebensegdec1:
 .dw SEG1A+SEG1B+SEG1C+SEG1D+SEG1E+SEG1F ;//0
 .dw SEG1B+SEG1C ;//1
 .dw SEG1A+SEG1B+SEG1D+SEG1E+SEG1G ;//2
 .dw SEG1A+SEG1B+SEG1C+SEG1D+SEG1G ;//3
 .dw SEG1B+SEG1C+SEG1F+SEG1G ;//4
 .dw SEG1A+SEG1C+SEG1D+SEG1F+SEG1G ;//5
 .dw SEG1A+SEG1C+SEG1D+SEG1E+SEG1F+SEG1G ;//6
 .dw SEG1A+SEG1B+SEG1C ;//7
 .dw SEG1A+SEG1B+SEG1C+SEG1D+SEG1E+SEG1F+SEG1G ;//8
 .dw SEG1A+SEG1B+SEG1C+SEG1D+SEG1F+SEG1G ;//9
siebensegdec2:
 .dw (SEG2A+SEG2B+SEG2C+SEG2D+SEG2E+SEG2F)>>16,(SEG2A+SEG2B+SEG2C+SEG2D+SEG2E+SEG2F)&0xFFFF ;0
 .dw (SEG2B+SEG2C)>>16 , (SEG2B+SEG2C)&0xFFFF ;//1
 .dw (SEG2A+SEG2B+SEG2D+SEG2E+SEG2G)>>16, (SEG2A+SEG2B+SEG2D+SEG2E+SEG2G)&0xFFFF ;//2
 .dw (SEG2A+SEG2B+SEG2C+SEG2D+SEG2G)>>16, (SEG2A+SEG2B+SEG2C+SEG2D+SEG2G)&0xFFFF ;//3
 .dw (SEG2B+SEG2C+SEG2F+SEG2G)>>16, (SEG2B+SEG2C+SEG2F+SEG2G)&0xFFFF ;//4
 .dw (SEG2A+SEG2C+SEG2D+SEG2F+SEG2G)>>16, (SEG2A+SEG2C+SEG2D+SEG2F+SEG2G)&0xFFFF ;//5
 .dw (SEG2A+SEG2C+SEG2D+SEG2E+SEG2F+SEG2G)>>16, (SEG2A+SEG2C+SEG2D+SEG2E+SEG2F+SEG2G)&0xFFFF ;//6
 .dw (SEG2A+SEG2B+SEG2C)>>16, (SEG2A+SEG2B+SEG2C)&0xFFFF ;//7
 .dw (SEG2A+SEG2B+SEG2C+SEG2D+SEG2E+SEG2F+SEG2G)>>16, (SEG2A+SEG2B+SEG2C+SEG2D+SEG2E+SEG2F+SEG2G)&0xFFFF ;//8
 .dw (SEG2A+SEG2B+SEG2C+SEG2D+SEG2F+SEG2G)>>16, (SEG2A+SEG2B+SEG2C+SEG2D+SEG2F+SEG2G)&0xFFFF ;//9
siebensegdec3:
 .dw (SEG3A+SEG3B+SEG3C+SEG3D+SEG3E+SEG3F)>>16, (SEG3A+SEG3B+SEG3C+SEG3D+SEG3E+SEG3F)&0xFFFF ;//0
 .dw (SEG3B+SEG3C)>>16, (SEG3B+SEG3C)&0xFFFF ;//1
 .dw (SEG3A+SEG3B+SEG3D+SEG3E+SEG3G)>>16, (SEG3A+SEG3B+SEG3D+SEG3E+SEG3G)&0xFFFF ;//2
 .dw (SEG3A+SEG3B+SEG3C+SEG3D+SEG3G)>>16, (SEG3A+SEG3B+SEG3C+SEG3D+SEG3G)&0xFFFF ;//3
 .dw (SEG3B+SEG3C+SEG3F+SEG3G)>>16, (SEG3B+SEG3C+SEG3F+SEG3G)&0xFFFF ;//4
 .dw (SEG3A+SEG3C+SEG3D+SEG3F+SEG3G)>>16, (SEG3A+SEG3C+SEG3D+SEG3F+SEG3G)&0xFFFF ;//5
 .dw (SEG3A+SEG3C+SEG3D+SEG3E+SEG3F+SEG3G)>>16, (SEG3A+SEG3C+SEG3D+SEG3E+SEG3F+SEG3G)&0xFFFF ;//6
 .dw (SEG3A+SEG3B+SEG3C)>>16, (SEG3A+SEG3B+SEG3C)&0xFFFF ;//7
 .dw (SEG3A+SEG3B+SEG3C+SEG3D+SEG3E+SEG3F+SEG3G)>>16, (SEG3A+SEG3B+SEG3C+SEG3D+SEG3E+SEG3F+SEG3G)&0xFFFF ;//8
 .dw (SEG3A+SEG3B+SEG3C+SEG3D+SEG3F+SEG3G)>>16, (SEG3A+SEG3B+SEG3C+SEG3D+SEG3F+SEG3G)&0xFFFF ;//9

segdec1:			;r0=Ziffer --> r16:r17:r18:r19 |= Codierung
	ldi ZL, low(siebensegdec1<<1)
	ldi ZH, high(siebensegdec1<<1)
	add r0, r0		;r0 *= 2
	add ZL, r0
	brcc dec18
	inc ZH
	rjmp dec18
segdec2:			;r0=Ziffer --> r16:r17:r18:r19 |= Codierung
	ldi ZL, low(siebensegdec2<<1)
	ldi ZH, high(siebensegdec2<<1)
	rjmp dec3L0
segdec3:			;r0=Ziffer --> r16:r17:r18:r19 |= Codierung
	ldi ZL, low(siebensegdec3<<1)
	ldi ZH, high(siebensegdec3<<1)
dec3L0:	lsl r0
	lsl r0			;r0 *= 4
	add ZL, r0
	brcc dec3L1
	inc ZH
dec3L1:	lpm r0, Z+
	or r17, r0
	lpm r0, Z+
	or r16, r0
dec18:	lpm r0, Z+
	or r19, r0
	lpm r0, Z
	or r18, r0
	ret

zahlauflcd:	
	lds r19, polari
	inc r19
	andi r19, 1		; Polaritaet, einmal auf 1 dann auf 0
	sts polari, r19
zahlauflcdanzeigen:	;zahlauflcdanzeigen(int zahl,int teiler,int polaritaet)
; r17:r16 = zahl, r18 = log(teiler), r19 = polaritaet,  werden ueberschrieben
; Beispiel: zahl=1234  r18=2 (teiler=100) --> Anzeige="12.34"
	push r0
	push r2
	push r3
	push r21
	push r20	; ulong n; r16:r17:r18:r19 = n
	push ZL
	push ZH
	mov r21, r19	;polaritaet
	cpi r18, 1	; switch(teiler)
	breq anzL1
	cpi r18, 2
	breq anzL2
	cpi r18, 3
	breq anzL3
	clr r18
	clr r19		;default: n=0; (r16,r17 weiter unten auf 0 gesetzt)
	rjmp anzL4
anzL1:	ldi r18, SEG1DP>>8
	ldi r19, 0		; case 10: n = SEG1DP; break;
	rjmp anzL4
anzL2:	ldi r18, 0
	ldi r19, SEG2DP		; case 100: n = SEG2DP; break;
	rjmp anzL4
anzL3:	ldi r18, 0
	ldi r19, SEG3DP		; case 1000: n = SEG3DP; break;
anzL4:	sbrs r17, 7		;if(zahl<0)
	rjmp anzL5
	clr r2
	clr r3
	sub r2, r16
	sbc r3, r17		; {zahl= -zahl; n |= SEGMinus;}
	ldi r16, SEGMinus>>24
	ldi r17, (SEGMinus>>16)&0xFF
	rjmp anzL6
anzL5:	mov r2, r16
	mov r3, r17
	clr r16
	clr r17
anzL6:		;Zahl ist jetzt in r3:r2, r16:r17:r18:r19 = initialisiertes n
;weggelassen if(zahl>1999) zahl=1999; //Fehler: darstellbare Zahlen auf 1999 beschraenkt
	ldi r20, 10
	rcall divmodr20	; z1=zahl%10; zahl/=10;
	rcall segdec1	; n += siebensegdec1[z1];
	rcall divmodr20	; z2=zahl%10; zahl/=10;
	rcall segdec2	; n += siebensegdec2[z2];
	rcall divmodr20	; z3=zahl%10; zahl/=10;
	rcall segdec3	; n += siebensegdec3[z3];
	tst r3			;if(zahl>999)
	brne anzBC		;fuer zahlen groesser 1999 trotzdem zuvorderst eine Eins anzeigen
	tst r2			;normalerweise steht hier nur eine 0 oder eine 1
	breq anzL7
anzBC:	ori r16, SEG4BC>>24	;   n |= SEG4BC;
anzL7:
	tst r21		; if(polaritaet!=0)
	breq anzL8
	ldi r20, 0xFF
	eor r16, r20		; n ^= 0xFFFFFFFF;
	eor r17, r20
	eor r18, r20
	eor r19, r20
anzL8:
	rcall datenschieben	; datenschieben(n);
	pop ZH
	pop ZL
	pop r20
	pop r21
	pop r3
	pop r2
	pop r0
	ret

;-----------------------------------------------------------------------------
; divmodr20
;  16Bit vorzeichenlose Zahl dividieren durch 8Bit vorzeichenlosen Teiler
; Eingabe: r3:r2 = Zahl (16Bit), r20 = Teiler (8Bit) (unveraendert)
; Ausgabe: r3:r2 = Resultat, r0 = Rest
;-----------------------------------------------------------------------------
divmodr20:
	push r17
	clr r0
	ldi r17, 16
divL1:	lsl r2
	rol r3
	rol r0
	cp r0, r20
	brcs divL2
	sub r0, r20
	inc r2
divL2:	dec r17
	brne divL1
	pop r17
	ret

; aus eeconvert.cc
tabelle:	;static int tabelle[]=
	;; Temparaturen 10*mehr damit wir keine Fliesszahlen brauchen
	;; -550 ist also -55.0 GradCelsius
	.dw -550, 990, -500, 1040, -400, 1146, -300, 1260, -200, 1381
	.dw -100, 1510, 0, 1646, 100, 1790, 200, 1941, 250, 2020
	.dw 300, 2100, 400, 2267, 500, 2441, 600, 2623, 700, 2812
	.dw 800, 3009, 900, 3214, 1000, 3426, 1100, 3643, 1200, 3855
	.dw 1250, 3955, 1300, 4048, 1400, 4208, 1500, 4323, 0, 0
umrechnen:	;double umrechnen(int sensor,int variante,int wert)
.def wertL=r0	; Eingabe: wert=gemessen von AD-Wandler
.def wertH=r1	; Rueckgabe: r1:r0 = GradCelsius*10
.def variante=r16	; 1 oder 3
.equ wr1=2000	;const double wr1=2000;
.equ wmax=1024	;const double wmax=1024;//Maximalwert 10-Bit-AD-Wandler + 1
	push r2
	push r3		;double r2,y; besser long wr2; int y
	push r16
	push r17
	push r18
	push r19
	push r20
	push r21
	push ZL
	push ZH
	cpi r16, 3		;variante==3 ?
	breq ohmumrechnen
;sonst variante==1 (und Sensor bisher nur dieser: sensor==222)
	ldi r16, low(wr1)	; wr2=wr1*wert/(wmax-wert);
	ldi r17, high(wr1)
	mov r18, r0
	mov r19, r1		;r19:r18 = wert
	mov r2, r16
	mov r3, r17
	rcall mul16		;r3:r2:r1:r0 = r1:r0 * r3:r2
	ldi r16, low(wmax)
	ldi r17, high(wmax)
	sub r16, r18
	sbc r17, r19
	rcall div16		;r3:r2:r1:r0 /= r17:r16
ohmumrechnen:
	mov r2, r0
	mov r3, r1		;r3:r2 = wr2 = Messwiderstand in Ohm
;;    int t0,t1;
;;    double w0,w1;
;;    for(i=0;tabelle[i+5]!=0 && wr2>tabelle[i+3];) i+=2;
;	clr r20			; i=0  (wegoptimiert)
	ldi ZL, low(tabelle<<1)
	ldi ZH, high(tabelle<<1)	; z = &tabelle[i]
forL1:	ldi r16, 5
	rcall getzi		; r17:r16 = z[5]
	tst r16
	brne forL2
	tst r17
	breq forende
forL2:	ldi r16, 3
	rcall getzi
	cp  r16, r2
	cpc r17, r3		; wr2>tabelle[i+3]?
	brcc forende		; nein-->
	adiw ZL, 4		;statt i+=2  z= &z[2] (4 wegen 2 Byte pro int)
	rjmp forL1
forende:
	lpm r18, Z+
	lpm r19, Z+		;r19:r18 = t0=tabelle[i];
	lpm r16, Z+
	lpm r17, Z+		;r17:r16 = w0=tabelle[i+1];
	lpm r0, Z+
	lpm r1, Z+		;r1:r0 = t1=tabelle[i+2];
	lpm r20, Z+
	lpm r21, Z+		;r21:r20 = w1=tabelle[i+3];
	sub r0, r18		;y=t0+(t1-t0)*(wr2-w0)/(w1-w0);
	sbc r1, r19		; (t1-t0)
	sub r2, r16
	sbc r3, r17		; (wr2-w0)
	rcall mul16		;r3:r2:r1:r0 = r1:r0 * r3:r2
	sub r20, r16
	sbc r21, r17		; (w1-w0)
	mov r16, r20
	mov r17, r21		;y=(t1-t0)*(wr2-w0)/(w1-w0)
	rcall div16		;r3:r2:r1:r0 /= r17:r16
	add r0, r18
	adc r1, r19		;y+=t0
	pop ZH
	pop ZL
	pop r21
	pop r20
	pop r19
	pop r18
	pop r17
	pop r16
	pop r3
	pop r2
	ret

getzi:	push ZL
	push ZH
	clr r17
	lsl r16
	add ZL, r16
	adc ZH, r17
	lpm r16, Z+
	lpm r17, Z+
	pop ZH
	pop ZL
	ret

mul16:		;r3:r2:r1:r0 = r1:r0 * r3:r2
	push r4
	push r5
	push r16
	push r17
	push r18
	push r19
	mov r16, r0
	mov r17, r1		; AB
	mov r18, r2
	mov r19, r3		; CD
	mul r17, r19		;A*C
	mov r2, r0		; EFGH = AB*CD --> EF=A*C GH=B*D EFG+=A*D+B*C
	mov r3, r1		; r3r2 = EF=A*C
	mul r16, r18		;B*D
	mov r4, r0
	mov r5, r1		; r5r4 = GH=B*D
	mul r17, r18		;A*D
	clr r17
	add r5, r0		; EFG+=A*D
	adc r2, r1
	adc r3, r17
	mul r16, r19		;B*C
	add r5, r0		; EFG+=B*C
	adc r2, r1
	adc r3, r17
	mov r0, r4
	mov r1, r5
	pop r19
	pop r18
	pop r17
	pop r16
	pop r5
	pop r4
	ret

div16:			;r3:r2:r1:r0 /= r17:r16, r17:r16 = Rest
	push r18
	push r19
	push r20
	clr r18
	clr r19
	ldi r20, 32
div16L1:lsl r0
	rol r1
	rol r2
	rol r3
	rol r18
	rol r19
	cp  r18, r16
	cpc r19, r17
	brcs div16L2
	sub r18, r16
	sbc r19, r17
	inc r0
div16L2:dec r20
	brne div16L1
	mov r16, r18
	mov r17, r19		;r17:r16 = Rest
	pop r20
	pop r19
	pop r18
	ret

thermostat_init:
	push r16
	push r17
	ldi r16, low(5000)	;auf grossen Wert setzen
	ldi r17, high(5000)	;(5000==500.0Grad Celsius)
	sts sollwertL, r16
	sts sollwertH, r17
	pop r17
	pop r16
	ret
thermostat:
	push r16
	push r17
	push r18
	push r19
	lds r18, istwertL
	lds r19, istwertH
	sbic PIND, 3		;2. Taster gedrueckt?
	rjmp the1		;nein-->
	sts sollwertL, r18	;ja: aktueller Sollwert setzen
	sts sollwertH, r19
the1:	lds r16, sollwertL
	lds r17, sollwertH	;Temperatur auf Sollwert halten
	cp  r18, r16
	cpc r19, r17		;Istwert<Sollwert ?
	brcs the2		;ja-->
	sbi PORTB, 3		;PB3 setzen
	rjmp theret
the2:	cbi PORTB, 3		;PB3 loeschen
theret:	pop r19
	pop r18
	pop r17
	pop r16
	ret

;-------Ende LCD-Anzeige und Thermostat ------------------------------------------------
.endif
