; *************************************************************************************************
; Demoprogramm: Verwendung der Analog-Compare-Einheit
; Sekundentakt zur Systemkontrolle
; Zähler 0 wird im CTC-Modus betrieben. Der Wert in OCRA wird so eingestellt, dass 
; am Pin PD4 die LED im Sekundentakt ein und aus geschaltet wird.
;
; Am Pin AIN1 (-) wird der Sensor (Mitte der Serienschaltung Rv - NTC) ageschlossen
; Am Pin AIN0 (+) liegt der Abgriff eines Potis an der Betriebsspannung (GND ... Vcc).
; Projektname: acompare.aps
; Dateiname:   acompare.asm
;
; *************************************************************************************************
;
	.nolist
	.include "tn2313def.inc"
	.list

.equ lcd_asm = 1
;.equ arithmetik_asm = 1			; muss declariert werden, wenn Hexadezimalzahlen und 
									; 3-Byte-Ganzzahlaritmetik gebraucht werden
.ifdef arithmetik_asm
	.include "arithmetik.asm"		; Die Datei steht im Verzeichnis avrlib
									; einbinden über:
									; Project - Assembleroptions - Additional include path
.endif
.equ toggle_test_pin = 0			; 

;
;	xtal-Frequenz dem Programm bekannt machen
	.equ xtal = 8000000				; 8 MHz

	.def temp1 = r16				; wird definieren zur Gaudi mal die Register
	.def temp2 = r17				; Namen neu, um zu zeigen, dass auch das geht
	.def temp3 = r18				; Die Register r16 bis r18 sind Mädchen für alles

	.def flag  = r19				; R19 nimmt Programmmerkerbits auf
		.equ fsekunde = 0			; Sekundenbit: Bit0
		.equ fanalog = 1			; Analogzähler wurde erhöht
		.equ fanalog_read = 2		; Ereigniszähler wurde angezeigt

	.def jiffies = r20				; Jiffies sind die Hundertstel-Sekundenbruchteile
	.def hundertstel = r21			; Hundertstelsekunden
	.def sekunden = r22				; selbstredend
	.def minuten = r23
	.def stunden = r24

	.def a_event = r27				; Ereigniszähler für Analogvergleichsereignisse
	.def a_timeout = r26			; Zähler für Entprellung
	.equ a_led_port = portd			; portd für die Status-LED
	.equ a_led_pin = 3				; pin 3
	.equ a_led_dr = ddrd			; ddrd
	.equ a_time_preset = 150		; Timeoutwert 1 .. 200 Hundertstelsekunden für Entprellung
	.equ acsr_cont = (1<<ACD) | (0<<ACBG) | (1<<ACI) | (1<<ACIE) | (0<<ACIC) | (0<<ACIS1) | (0<<ACIS0)
	  								; Analog Compare Control und Statusregister setzen
									; ACBG = 0 -> Externe Referenz
									; ACI = 1 -> IRQ-Flag löschen durch schreiben einer 1
									; ACIE = 1 -> AnalogCompare Interrupt zulassen
									; ACIC = 0 -> keine Inputcapture IRQs durch Timer1
									; ACSI1:ACSI0 = 0 0 -> Analog Comp. IRQ bei ACO-Toggle

	.equ p = portd					; portd für die LED
	.equ pin = 4					; pin 4
	.equ dr = ddrd					; ddrd
	.equ ledmask = (1<<pin)			; Maske zum Umschalten mit eor-befehl
									; Die Pseudoassembleranweisung 1<<pin bedeutet, dass 
									; in einem Byte eine 1 um so viele Stellen nach links
									; geschoben wird, wie der Wert in pin angibt.
									; 0b0000001 4 mal links schieben: 0b00010000 = 0x10
									; die 1 steht jetzt an der Bitnummer 4
									; Position: 76543210
									; Bytewert: 00010000
									; Das ist in der Tat die Leitung, welche die LED steuert.
	.equ testpin = 2				; Portleitung für Testsignalausgabe
	.equ testport = portd
	.equ testddr  = ddrd			; Port für Testsignalausgabe

.ifdef lcd_asm 						; Wenn ein LCD angeschlossen ist, brauchen wir das
	.equ LCD_PORT = PORTB
	.equ LCD_DDR  = DDRB
	.equ rs_port = portd
	.equ rs_ddr = ddrd
	.equ e_port = portd
	.equ e_ddr = ddrd				;
	; Datenbits: LCD_PORT.4..7		; just for info
	.equ maskdata = 0b11110000		; Maske für Datenleitungen
	.equ maskport = ~maskdata		; Invertieren für Portmaske
	.equ PIN_RS   = 6
;	.equ PIN_E2   = 5				; not used
	.equ PIN_E    = 5
	; LCD-Pin -RW liegt auf Masse	
.endif

	.ifndef XTAL
	.equ XTAL = takt
	.endif


	.equ vergleichswert = 200		; Wert für ocr1a-Vergleichsregister -> 200 µs
	.equ jfactor = 50				; Faktor zum Erreichen eines Jiffies -> 1 Jiffie=10ms=0,01s
	.equ ledan = 100-10				; Festlegen des Dutycycles (Anzeit)
									; 100-10, weil LED  auf Sensorplatine highaktiv ist

; ************************************************************************************************
; * IRQ-Vektoren
; ************************************************************************************************
	.org 0x0000						; Interrupttabelle
	rjmp start ; Reset Handler
	.org 0x000a
	rjmp ANA_COMP ; Analog Comparator Handler
	.org 0x000d
	rjmp Timer0_oc0a ; Timer0 Compare A Handler


.org 0x0013						; Programmstart hinter allen IRQ-Vektoren
; ************************************************************************************************
; * Hauptprogramm Vorbereitungen
; ************************************************************************************************

Start:
	ldi temp1, low(ramend)			; Stackpointer setzen
	out spl, temp1					
	ldi temp1, (1<<pin)	 | (1<<a_led_pin); Bit 4 von PortD als Ausgang
	out dr, temp1					; setzen
									; Wir haben jetzt noch eine ganze Menge weiterer
									; Vorbereitungen für die Zeitmessung zu treffen
									; Die Zeitmessung wird vom Hauptprogramm ausgeklinkt
									; und dem Interruptprozess eines Timers überlassen
	; Testpin setzen
	sbi	testddr,testpin				; Testpin als Ausgang
	ldi temp1, (0<<pin) | (0<<a_led_pin)
	out p, temp1					; LED aus

	; Timer0 einrichten
	ldi temp1, 0					; Systemtakt für Timer
	out tccr0b, temp1				; ausschalten
	
	ldi temp1, (1<<wgm01)			; CTC-Mode 2 einstellen, Zählen bis OCR0A 
	out tccr0a, temp1
	
	ldi temp1, vergleichswert-1		; OCR0A setzen
	out ocr0a, temp1

	ldi temp1, (1<<ocie0a)			; output compare 1A IRQ für Timer 0 zulassen
	out timsk, temp1

	ldi temp1, (1<<cs01)			; Systemtakt durch 8 als takt für timer 0 
	out tccr0b, temp1				; Takt einschalten

	
	; LCD einrichten	
	rcall lcd_init					; LCD initialisieren
	rcall lcd_clear					; Anzeige löschen
	rcall say_hallo					; Begrüßung ausgeben
	
	;Analogcompare-Ereignisse vorbereiten und zum IRQ´zulassen
	ldi temp1, (1<<AIN1D) | (1<<AIN0D)
	out didr, temp1					; Digitalfunktion für die Analogvergleicherpins ausschalten

	ldi temp1, acsr_cont			; so wie im Kopfbereich definiert
	out acsr, temp1					; Analog Compare Control und Statusregister setzen
	cbi acsr, acd					; Analogcomparator einschlaten

	; fast fertig mit Vorbereitungen
	sei								; beliebige Interrupts grundsätzlich zulassen
	clr sekunden					; 

init01:
	cpi sekunden, 3
	brne init01
	rcall lcd_clear					; 3 Sekunden abwarten, dann LCD löschen

init99:
	ldi flag, 0b00000000			; Flag für "volle Sekunde" löschen
									; Flag für "Analogereignis aufgetreten" löschen
	clr jiffies
	clr hundertstel					; Zeitrechnung auf 0 stellen
	clr sekunden					; 
	clr minuten
	clr stunden						; Zeitrechnung auf 0 stellen
									; Display an, Unterstrichcursor aus, kein Cursorblinken
    ldi temp1, (1<<dispon) | (0<<curson) | (0<<cursbl)
	rcall lcd_cursor				; einstellen

	clr a_event						; Eventzähler für Analogereignisse löschen

	
; -------------------------------------------------------------------------------------------------
loop:								; Hauptschleife mit Job-Unterprogrammen
	
	sbrc flag, fsekunde				; wenn volle Sekunde noch nicht vorbei, überspringe Sub-Aufruf 
	rcall anzeige_update			; Zeitanzeige updaten
	sbrc flag, fanalog				; wenn kein Analogereignis aufgetreten, überspringe Sub-Aufruf
	rcall analog_ereignis			; Analogergebnisanzeige updaten
	rjmp loop						; Ende der Hauptschleife
; -------------------------------------------------------------------------------------------
; Unterprogramme der Jobschleife
; -------------------------------------------------------------------------------------------							
anzeige_update:	
	rcall lcd_home					; Anzeige löschen
	mov temp1, stunden				; zuerst die Stunden 
	rcall lcd_number				; ausgeben
	ldi temp1, ':'
	rcall lcd_data					; ":" als Trennzeichen ausgeben
	mov temp1, minuten				; zuerst die Stunden 
	rcall lcd_number				; ausgeben
	ldi temp1, ':'
	rcall lcd_data					; ":" als Trennzeichen ausgeben
	mov temp1, sekunden				; zuerst die Stunden 
	rcall lcd_number				; ausgeben
	cbr flag, 1<<fsekunde			; Flag für volle Sekunde löschen
									; In dieser Version wurde durch
									; * Verkürzen der Befehlzeiten für lcd-clear und lcd_home auf 1,67ms
									; * Auslagern der Cursorsteuerung
									; * entfernen des lcd_clear-Aufrufs 
									; die Updatezeit von 0,0158s auf 0,0022s reduziert
									; das bedeutet, dass nur ca. 0,25 % der Prozessorleistung
									; auf die Zeitausgabe entfallen, der Rest ist Leerlauf
									; un kann von anderen Prozessen genutzt werden
	ret								; zurück vom UP

; -------------------------------------------------------------------------------------------							
analog_ereignis:
	ldi 	temp2, 1				; Positionieren den Cursor in Zeile 2 und
	ldi 	temp1, 0				; Spalte 1 -
	rcall 	lcd_position			; Cursor positionieren
	mov 	temp1, a_event			; Eventzähler zur Ausgabe vorbereiten
	rcall 	lcd_number				; ausgeben
	ldi 	temp1, ' '				; Trennzeichen
	rcall 	lcd_data				; " " ausgeben
	ldi 	zl, low(fl_event*2)		; Adresse des Strings "Event(s)" im Programmspeicher
	ldi 	zh, high(fl_event*2)	; 
	rcall 	lcd_flash_string		; Stringausgeben (ohne das Nullbyte)
	cbr 	flag, 1<<fanalog		; Analogbit löschen
	sbr 	flag, 1<<fanalog_read	; Wir haben den Ereigniszähler gelesen und angezeigt
	ret								; zurück vom UP
fl_event:
	.db "Event(s) ",0				; Der String muss mit einem 0-Byte abgeschlossen sein!
; -------------------------------------------------------------------------------------------							
; Ende der Unterprogramme der Jobschleife
; -------------------------------------------------------------------------------------------							
;
;
; *************************************************************************************************
; *   Interrupt - Unterprogramme
; *************************************************************************************************
; Output compare Interrupt - Service Routine

timer0_oc0a:
	push temp1						; temp1 sichern
	in temp1, sreg					; Statusregister sichern

	sbi testport, testpin			; Testpin Testport.pin auf 1

	inc jiffies						; Jiffies erhöhen


	; ------------------------------- IRQ - Aufgaben ----------------------------------------------
	cpi jiffies, 1					; 
	brne job2
	; Job 1   Zeitscheibe 1: Tastenabfrage ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

	; keine Verwendung

job2:
	cpi jiffies, 2					; 
	brne job3
	; Job 2   Zeitscheibe 2: Tastenaktionen +++++++++++++++++++++++++++++++++++++++++++++++++++++++
	; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	
	; keine Verwendung
		
job3:
	; ------------------------------- Tasten - Aufgaben beendet------------------------------------
	;
	; ------------------------------- Zeitupdate --------------------------------------------------
	
										

	cpi jiffies, jfactor			; so lange 50 nicht erreicht ist, fertig
	brne end_isr					; ergibt: 80000000Hz/8/200 = 5000 Hz (DC=0,4%)
	
									; es ist der wert 50 erreicht
	clr jiffies						; Jiffies löschen, 50 * 200µs = 10000µs=10ms=0,010s

	inc hundertstel					; hundertstelsekunden erhöhen

	cpi hundertstel, 100-ledan		; Für die letzten ledan hundertstel sekunden
	brne led_fertig
	cbi p, pin						; led einschalten, (wenn Dutycycle-Wert erreicht)

led_fertig:	
	cpi a_timeout, 0				; a_timeout auf 0 testen
	breq a_timeout_checked			; leave it untouched if 0
	dec a_timeout					; decrementieren, falls noch nicht = 0
	rjmp analog_done				; und fertig mit der Behandlung des Analogteils
a_timeout_checked:
	sbrc flag, fanalog_read			; wenn fanalog_read = 0, hat Änderung stattgefunden
	rjmp analog_done				; wenn fanalog_read = 1, kein Anzeigebedarf mehr
	in temp1, acsr					; Momentanen Zustand des Comparatorausgangs einlesen
	andi temp1, 1<<aco				; ACO-Bit isolieren
	breq a_led_aus					; Bit = 0 -> zum Ausschalten der LED
a_led_an:
	inc a_event						; Bit = 1 -> Ereignis zählen
	sbr	flag, 1<<fanalog			; Flag für stattgefundenes Ereignis setzen
	sbi a_led_port,a_led_pin		; LED einschalten (Highaktiv über Treiber und LED-Array)
	rjmp switching_done				; fertig
a_led_aus:		
	cbi a_led_port,a_led_pin		; LED ausschalten (Highaktiv über Treiber und LED-Array)
switching_done:
analog_done:						; weiter im normalen Zeitablauf
	cpi hundertstel, 100			; eine Sekunde erreicht?
	brne fertig						; nein
									; ja
	clr hundertstel					; Hundertstelsekunden auf 0 rücksetzen
	sbi p, pin						; und am Ende der 100 hundertstel Sekunden: LED aus
	sbr flag, 1<<fsekunde			; Hier oder im Verlauf der IRQ-Routine können Flags
									; für das Hauptprogramm gesetzt werden. 
									; hier wird dem Hauptprogramm mitgeteilt, dass eben eine 
									; Sekunde geschaltet wird
	inc sekunden					; sekunden erhöhen
	cpi sekunden, 60
	brne fertig						; Minute noch  nicht erreicht?
									; sonst Sekundenzähler löschen
	clr sekunden					; Sekundenzähler läuft  von 0 bis 59(60)
									; Hier könnte sich ein Minuten, Stunden, Tage...Zähler 
									; anschließen.
	inc minuten	
	cpi minuten, 60		
	brne fertig

	clr minuten
	inc stunden
	cpi stunden, 24
	brne fertig

	clr stunden

fertig:
	nop								; Evtl. weitere Standardaufgaben

end_isr:							; Ende der ISR Timer_OC0A
	cbi testport, testpin			; Testpin PortD.pin+1 auf 0
	out sreg, temp1					; status register restaurieren
	pop temp1						; temp1 zurück
	reti							; irq-routine beenden

; ---------------------------------------------------------------------------------------
; ISR Analog Comparator
; ---------------------------------------------------------------------------------------
ANA_COMP:							; Behandlung des Analog-Vergleichs
	push temp1						; Retten der Register
	in temp1, sreg					; 
	push temp1						; Diese ISR setzt bei jedem Wechsel des Comparatorausgangs
	push temp2						; den Timeout-Zähler des Analogvergleichs auf Startwert

	ldi a_timeout, a_time_preset	; Wechsel festgestellt und entprelltimer gestellt
	cbr flag, 1<<fanalog_read		; Anzeige vorbereiten, 
									; sobald Zustand konstant über Jobroutine ans LCD

ana_comp_exit:						; wenn nach a_time_preset Hundertstelsekunden kein 
	pop temp2						; Wechsel mehr stattgefunden hat, wird durch die 
	pop temp1						; ISR des Timer0 die LED geschaltet und das Ereignis gezählt
	out sreg, temp1
	pop temp1						; Register restaurieren
	reti							; Endes der ISR ANA_COMP


; ****************************************************************************************************
; 
; Unterprogramme Betriebssystem
;
; ****************************************************************************************************
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                 LCD-Routinen                ;;
;;                 ============                ;;
;; 4bit-Interface                              ;;
;; Portbelegung siehe                          ;;
;; Hauptprogramm                               ;;
;;                              			   ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
; Die Register temp1 ... temp3 sind im Hauptprogramm zu declarieren
; Die Quarzfrequenz XTAL ist im Hauptprogramm als TAKT zu declarieren
; Der Daten/Befehlsport und die Steuerleitungen sind im Hauptprogramm deklariert

	.ifndef XTAL
	.equ XTAL = takt
	.endif
;
; Das Definitionsfile für den Prozessortyp wird im Hauptprogramm eingebunden
; ==============================================================================
; interface
; ==============================================================================
;
; Falls die Variable duodisplay im Hauptprogramm definiert ist,
; werden die Routinen dafür eingebunden
;
; mit gesetztem T-Flag wird die untere Hälfte von Displays mit 
;		zwei Controllern angesteuert
;
; lcd_data (temp1) - ein Zeichen in Temp1 übergeben zur Ausgabe
; lcd_command (temp1) - einen Befehlscode in temp1 übergeben
; lcd_enable - Übernahmebefehl (strobe)
; lcd_enable2 - bei 4-zeiligem Display die unteren 2 Zeilen
; delay50us - 50 us Sekunden Pause machen
; delay5ms - 5 ms warten
; lcd_init - Display initialisieren (4Bit Betrieb)
; Powerupwait - Warten zum Betriebsstart, bis Spannung steht
; lcd_clear - Display löschen, Cursor home
; lcd_home - Cursor in Homeposition
; lcd_position - Cursor an Position Spalte (temp1) Zeile (temp2), 
; lcd_cursor - Display-Cursor-Blinken an/aus
;		Bit2 -> Display; Bit1 -> cursor; Bit0 -> Blinken
; lcd_flash_string - einen String aus dem Programmspeicher (Flash) ausgeben
;       der String ist 0-terminiert, das z-Register enthält die Textadresse (mal 2!)
;       ldi zl, low(textadresse*2)
;       ldi zh, high(textadresse*2)
; lcd_number (temp1) - Zahl in Register temp1 dezimal ausgeben
; lcd_number_hex (temp1) - Zahl in Register temp1 als Hexadezimalzahl ausgeben.
; lcd_binout - Binärzahl in akku b ausgeben, Binär-Stellenzahl in temp2
;		mit bin2asc nach buffer wandeln, Ziffern-Stellenzahl + 1 in temp2
; 		der bufferzeiger z steht auf der Null des String-Endes
; ==============================================================================
; interface ende
; ==============================================================================


.equ entry_mode    = 0b00000100	; Cursorbewegung und Displayverschiebung 
.equ inccp = 1 		; increase Cursor Position  bei Ausgabe eines Zeichens
.equ sdisp = 0		; scroll Display, wenn Cursor Randposition erreicht hat

.equ onoff_control = 0b00001000	; Display, Cursor, Cursor Blinken - an / aus
.equ dispon = 2 	; Display off
.equ curson = 1		; cursor ein
.equ cursbl = 0		; cursor blinken lassen

.equ cursor_scroll = 0b00010000	; Cursor move, Display scroll, links/rechts
.equ dispscr = 3	; setzen = display scrollen / clear = cursor bewegen
.equ movright = 2	; setzen = rechts / clear = links

.equ config =        0b00100000	; Interface Configuration, zeilenmodus, font
.equ achtbit = 4	; Datenbreite 1 = 8 Bit/ 0 = 4 bit an B4..B7
.equ twoline = 3	; 1 = zweizeilig / 0 = einzeilig
.equ bigfont = 2	; 1 = 5x11 Pixel / 0 = 5x7 Pixel

.equ cram_addr = 0b01000000	; Character RAM Adresse setzen
							; 0b01aaaaaa enthält die Zeichenram-Adresse, ab der 
							; geschrieben werden soll. Es folgen 8 Byte, welche 
							; die Pixeldaten des zu definierenden Zeichens enthalten
							; !!! Befehl mit "Display RAM Address Set" abschließen!!!
							; Ausführungszeit 40 µs

.equ dram_addr = 0b10000000	; Positioniert den Cursor neu an die übergebene Adresse
							; 0b1zaaaaaa 
.equ zeile = 6				; Zeilennummer Bit
							; z = Zeilennummer 0 oder 1
							; aaaaaa = 6-Bit Spaltennummer (0..63)
							; Ausführungszeit 40 µs
;
.equ t50us = ( XTAL * 50 / 5 ) / 1000000
.equ t5ms = ( XTAL * 5 / 606 ) / 1000
;
 ; ============================================================================
 ; =============================================================================
 .cseg
 lcd_data:
 		   push  temp1
           push  temp2
           push  temp3
		   in 	 temp3, sreg			 ; Statusregister sichern
		   push  temp3					 ; 
           mov   temp2, temp1            ; "Sicherungskopie" für
                                         ; die Übertragung des 2.Nibbles
           andi  temp1, maskdata	     ; unteres Nibble auf Null setzen
		   sbi   rs_port, PIN_RS         ; entspricht 0b00010000
           in    temp3, LCD_PORT
		   andi  temp3, maskport
           or    temp1, temp3
           out   LCD_PORT, temp1         ; ausgeben
		   rcall lcd_enable
           swap  temp2                   ; Vertauschen
           andi  temp2, maskdata         ; untere Hälfte auf Null setzen 
           sbi   rs_port, PIN_RS         ; entspricht 0b00010000
           or    temp2, temp3
           out   LCD_PORT, temp2         ; ausgeben
		   rcall lcd_enable
			                             ; 2. Nibble, kein swap da es schon
                                         ; an der richtigen stelle ist
										 ; Enable-Routine aufrufen
           rcall delay50us               ; Delay-Routine aufrufen

           pop   temp3
		   out   sreg,  temp3
		   pop   temp3
           pop   temp2
		   pop   temp1
           ret                           ; zurück zum Hauptprogramm
 
     ;
lcd_command:                            ; wie lcd_data, nur mit RS = 0
           push  temp1					; nicht für die Befehle clear und home (1,67 ms!)
		   push  temp2					; Wartezeit am Schluss: 50 µs
           push  temp3
		   in temp3, sreg
		   push temp3

		   cbi   rs_port, PIN_RS        ; 

           mov   temp2, temp1
           andi  temp1, maskdata		; High-Nibble zuerst
           in    temp3, LCD_PORT
		   andi  temp3, maskport
           or    temp1, temp3
           out   LCD_PORT, temp1
		   rcall lcd_enable
		   cbi   rs_port, PIN_RS        ; 
           swap  temp2					; Nibbles in der Sicherung tauschen
           andi  temp2, maskdata		; same prodedure für das Lownibble
           or    temp2, temp3
           out   LCD_PORT, temp2
		   rcall lcd_enable
           rcall delay50us
 
 		   pop temp3
		   out sreg, temp3
           pop   temp3
           pop   temp2
		   pop   temp1
           ret
 

  ; erzeugt den Enable-Puls
lcd_enable:
           sbi   e_PORT, PIN_E          ; Enable high
           nop                          ; 3 Taktzyklen warten
           nop
           nop
		;  rcall taste
           cbi   e_PORT, PIN_E          ; Enable wieder low
           ret  
		                           ; Und wieder zurück                     
		                   
; Pause nach jeder Übertragung
delay50us:                              ; 50us Pause
		   push temp1
		   in temp1, sreg
		   push temp1

           ldi   temp1, t50us
delay50us_:
		   nop
		   nop
           dec   temp1
           brne  delay50us_
		   pop temp1
		   out sreg, temp1
		   pop temp1
           ret                          ; wieder zurück
 
 ; Längere Pause für manche Befehle
delay5ms:                               ; 5ms Pause
		   push temp1
		   in temp1, sreg
		   push temp1
		   push temp2
           ldi  temp1, t5ms
		   rjmp wgloop0

delay2ms:  		   
		   push temp1
		   in temp1, sreg
		   push temp1
		   push temp2
           ldi  temp1, t5ms/3     		 ; ergibt 1,67 ms

WGLOOP0:   ldi  temp2, $C9
WGLOOP1:   dec  temp2
           brne WGLOOP1
           dec  temp1
           brne WGLOOP0

		   pop temp2
		   pop temp1
		   out sreg, temp1
		   pop temp1
           ret                          ; wieder zurück
 
 ; Initialisierung: muss ganz am Anfang des Programms aufgerufen werden
lcd_init:
           push  temp1
		   push  temp2
		   in    temp2, sreg
		   push  temp3

           in    temp1, LCD_DDR
           ori   temp1, maskdata
           out   LCD_DDR, temp1			; Datenleitungen auf Ausgang
		   sbi	 rs_ddr, pin_rs			; RS-Leitung aus Ausgang
		   sbi   e_ddr, pin_e			; E-Leitung auf Ausgang
           ldi   temp3,6				; 6 mal 5 ms = 30 ms warten vorbereiten
		   cbi   rs_port, PIN_RS        ; Es folgen Befehle
		   cbi	 e_port, pin_e 			; Leitung E auf 0
powerupwait:
           rcall delay5ms
           dec   temp3
           brne  powerupwait			; 30 ms warten
           ldi   temp1,    0b00000011   ; muss 3mal hintereinander gesendet
           out   LCD_PORT, temp1        ; werden zur Initialisierung
           rcall lcd_enable             ; 1
           rcall delay5ms
           rcall lcd_enable             ; 2
           rcall delay5ms
           rcall lcd_enable             ; und 3mal!
           rcall delay5ms
           ldi   temp1,    0b00000010   ; Cursor home
           out   LCD_PORT, temp1
           rcall lcd_enable
           rcall delay5ms				; 4bit-Modus einstellen
           ldi   temp1,    0b00101100   ; 4 Bit, 2 Zeilen, 5x11
           rcall lcd_command
           ldi   temp1,    0b00001111   ; Display on, Cursor on, Cursor blinkt
           rcall lcd_command
           ldi   temp1,    0b00000100   ; endlich fertig
           rcall lcd_command
		
		   pop   temp3
		   out   sreg, temp2
		   pop   temp2
           pop   temp1
           ret
 

 ; Sendet den Befehl zur Löschung des Displays
lcd_clear:
           push  temp1
           ldi   temp1,    0b00000001   ; Display löschen
           rcall lcd_command
           rcall delay2ms
           pop   temp1
           ret

 ; Cursor Home
lcd_home:
           push  temp1
           ldi   temp1,    0b00000010   ; Cursor Home
           rcall lcd_command
           rcall delay2ms
           pop   temp1
           ret

 ; Cursor an/aus
 ; Bit2 -> Display; Bit1 -> cursor; Bit0 -> Blinken
lcd_cursor:
		push 	temp1
		andi 	temp1, 0x07
		ori 	temp1, onoff_control
		rcall 	lcd_command
		rcall 	delay50us
		pop 	temp1
		ret

 ; Cursor an Position Spalte (temp1) Zeile (temp2), 
lcd_position:
		push 	temp1
		push 	temp2
		andi 	temp1, 0b00011111
		andi 	temp2, 0x01
		sbrc 	temp2, 0
		ldi 	temp2, (1<<zeile)
		or  	temp1, temp2
		sbr 	temp1, 0b10000000
		rcall 	lcd_command
		pop 	temp2
		pop 	temp1
		ret

 ; Einen konstanten Text aus dem Flash Speicher
 ; ausgeben. Der Text wird mit einer 0 beendet
lcd_flash_string:
           push  temp1
		   in 	 temp1,sreg
		   push  temp1					; Register sichern

lcd_flash_string_1:
           lpm   temp1, Z+				; Byte von der Position, die in ZL:ZH adressiertist
		   cpi   temp1, 0				; nach temp1 einlesen und auf Null-Byte vergleichen
           breq  lcd_flash_string_2		; Falls Nullbyte, dann fertig
           rcall lcd_data				; sonst auf LCD ausgeben
           rjmp  lcd_flash_string_1		; und noch einmal

lcd_flash_string_2:
		   pop   temp1					; Register restaurieren
		   out 	 sreg, temp1
           pop   temp1
           ret


.ifdef aritmetik_asm
; eine Zahl aus dem Register temp1 hexadezimal ausgeben
lcd_number_hex:
        push  temp1			; sichern
		push  temp2
		rcall ascbyte
		rcall lcd_data
		mov temp1, temp2
		rcall lcd_data
		pop   temp2
		pop   temp1
		ret
.endif
		

.ifndef lcd_number
	; ein Byte als Dezimalzahl auf LCD oder via RS232 ausgeben
	; Datenbyte: temp1 
lcd_number:
           push  temp1
           push  temp2
           push  temp3
		   in    temp3, sreg
		   push  temp3

		   ldi 	 temp2, low(lcd_data) 	  ; Adresse von Befehl "lcd_data" als
		   mov   zl, temp2 				  ; Sprungadresse für indirekten SUB-Call
		   ldi   temp2, high(lcd_data)	  ; in Z-Register ablegen
		   mov   zh, temp2
		   rjmp  v24_number_0

v24_number_0:
           mov   temp2, temp1
                                  		  ; abzählen wieviele Hunderter
                                          ; in der Zahl enthalten sind
;		   rjmp  v24_number_2a
           ldi   temp1, '0'
v24_number_1:
           subi  temp2, 100
           brcs  v24_number_2
           inc   temp1
           rjmp  v24_number_1
                                          ;
                                          ; die Hunderterstelle ausgeben
v24_number_2:
		   mov   temp3, temp1			  ; wert von temp1 merken
		   cpi   temp1, '0'
		   breq  v24_number_2a			  ; führende Null nicht ausgeben
           icall 
v24_number_2a:
           subi  temp2, -100              ; 100 wieder dazuzählen, da die
                                          ; vorherhgehende Schleife 100 zuviel
                                          ; abgezogen hat

                                          ; abzählen wieviele Zehner in
                            			  ; der Zahl enthalten sind
           ldi   temp1, '0'
v24_number_3:
           subi  temp2, 10
           brcs  v24_number_4
           inc   temp1
           rjmp  v24_number_3

                                          ; die Zehnerstelle ausgeben
v24_number_4:
           icall 
           subi  temp2, -10               ; 10 wieder dazuzählen, da die
                                          ; vorhergehende Schleife 10 zuviel
                                          ; abgezogen hat

                                          ; die übrig gebliebenen Einer
                                          ; noch ausgeben
           ldi   temp1, '0'
           add   temp1, temp2
           icall 
		   
		   pop   temp3
		   out   sreg, temp3
           pop   temp3
           pop   temp2
           pop   temp1
           ret
.endif


.ifdef aritmetik_asm
	; Binärzahl in akku b ausgeben, Binär-Stellenzahl in temp2
	; mit bin2asc nach buffer wandeln, Ziffern-Stellenzahl + 1 in temp2
	; der bufferzeiger z steht auf der Null des String-Endes
lcd_binout:
	ldi temp2, 24
	rcall bin2asc
lcd_bin2asc_0:
	ld temp1, -z
	rcall lcd_data
	dec temp2
	brne lcd_bin2asc_0
	ret
.endif	


; ****************************************************************************************************
; 
; Unterprogramme Anwendung
;
; ****************************************************************************************************
say_hallo:					; sagt "HALLO***" am LCD-Display
	ldi temp1, 'H'
	rcall lcd_data
	ldi temp1, 'A'
	rcall lcd_data
	ldi temp1, 'L'
	rcall lcd_data
	ldi temp1, 'L'
	rcall lcd_data
	ldi temp1, 'O'
	rcall lcd_data
	ldi temp1, '*'
	rcall lcd_data
	ldi temp1, '*'
	rcall lcd_data
	ldi temp1, '*'
	rcall lcd_data
	ret



.exit
;
; ******************************************************************************************
; * Ende für Programmcode 
; ******************************************************************************************

.if toggle_test_pin == 1
	; toggle Testpin
	sbis testport, testpin
	rjmp tbs
tbc: 		
	cbi testport, testpin
	rjmp tbready
tbs:
	sbi testport, testpin
tbready:
.endif




;0x0000;
rjmp start ; Reset Handler
;0x0001 
reti; rjmp INT0 ; External Interrupt0 Handler
;0x0002 
reti; rjmp INT1 ; External Interrupt1 Handler
;0x0003 
reti; rjmp TIM1_CAPT ; Timer1 Capture Handler
;0x0004 
reti; rjmp TIM1_COMPA ; Timer1 CompareA Handler
;0x0005 
reti; rjmp TIM1_OVF ; Timer1 Overflow Handler
;0x0006 
reti; rjmp TIM0_OVF ; Timer0 Overflow Handler
;0x0007 
reti; rjmp USART0_RXC ; USART0 RX Complete Handler
;0x0008 
reti; rjmp USART0_DRE ; USART0,UDR Empty Handler
;0x0009 
reti; rjmp USART0_TXC ; USART0 TX Complete Handler
;0x000A 
rjmp ANA_COMP ; Analog Comparator Handler
;0x000B 
reti; rjmp PCINT ; Pin Change Interrupt
;0x000C 
reti; rjmp TIMER1_COMPB ; Timer1 Compare B Handler
;0x000D 
rjmp Timer0_oc0a ; Timer0 Compare A Handler
;0x000E 
reti; rjmp TIMER0_COMPB ; Timer0 Compare B Handler
;0x000F 
reti; rjmp USI_START ; USI Start Handler
;0x0010 
reti; rjmp USI_OVERFLOW ; USI Overflow Handler
;0x0011 
reti; rjmp EE_READY ; EEPROM Ready Handler
;0x0012 
reti; rjmp WDT_OVERFLOW ; Watchdog Overflow Handler




	sbrc temp2, aco					; ACO ist aus, -AIN1(Sens) < +AIN0(Ref) -> überspringen
	rjmp aco_ein					; geh zum LED einschalten
aco_aus:
	cbi a_led_port, a_led_pin		; Status-LED aus
	rjmp ana_comp_exit
aco_ein:
	inc a_event						; Eventzähler erhöhen
	sbi a_led_port, a_led_pin		; Status-LED ein
