
; champOS v20, 2/2010 
; Champ v20 Operating System (6-chl. Photo-Amplifier)
; 
; Processor Atmel AVR ATMega8-16AU
; PCB-board: CHAMP6v20
; heinz@gfai.de
; http://www.gfai.de/~heinz/techdocs/index.htm
; 
; Developed with Atmel AVR-Studio v4.15
; Zur Arbeit empfiehlt es sich, das Programm mit AVR-Studio zu öffnen und zu compilieren.
; Menue/Build/Build (F7) produziert Unterprogramm-Labels im "Project"-Ordner. 
; Labels, die mit Großbuchstaben beginnen, sind Unterprogramme, Hilfslabels beginnen 
; mit Kleinbuchstaben. Alle Hilfslabels sind alphabetisch den UPs untergeordnet.
; Eine C-Version des Programms war in Arbeit, wurde aber verworfen, da zeitlich 
; kritische Programmteile immer wieder als Misthaufen übersetzt wurden.
; Diese Ausführung in ASM versteht sich als Versuch, die Grenzen der 
; Assembler-Programmierung durch etwas Systematik hinauszuschieben.
; Kleine Prozessoren leisten enorm viel, werden sie nur richtig programmiert.
; Sucht man nach einem Detailprogramm (z.B. EEPROM brennen), dann einfach Label mit 
; passendem Großbuchstaben doppelklicken (z.B. EEPROM_BurnValue).

.equ pgmversion = 0x21	; aktuelle Programmversion
.include "m8def.inc"	; ATmega8-Definitionsdatei

; All rights reserved (c): Gerd Heinz, GFaI Berlin
; Redistribution and use in source and binary forms, with or without
; modification allowed. 
; 
; 
; Funktionen:
; 6-Kanal Photodiodenverstärker, der Daten am Brenner einer Schweißmaschine 
; sammeln kann (in extrem EMV-haltiger Umgebung).
; Die Verstärkung kann vom Windows-Hyperterminal aus für jeden Kanal mit 8 Bit (0 bis 255)
; eingestellt werden.
; Verstärkungen werden im EEPROM des ATmega gespeichert und nach jedem RESET 
; an die Potis Typ Microchip MCP42xxx ausgegeben, siehe auch Schaltplan.
; Kommunikation mit PC (Hyperterminal) deshalb über eine isolierte RS485 (simplex).
; Am PC dient ein isolierter USB zu RS485-Adapter (USB2SERIALv2) zur Umsetzung auf USB.
; Version v10 steuerte Chopperverstärker, diese Funktionen sind in v20 stillgelegt, 
; aber noch rudimentär im Code zu finden. Der Name champOS war ursprünglich entstanden 
; als "Chopper Amplifier Operating System" (champOS). Version v10 des Champ zeigte, daß 
; die Verstärkung nicht so hoch gewählt werden muß, um Chopper wirklich zu brauchen. 
; Bei v20 sind Chopper-Funktionen entfernt (ausgetauscht).
; Sollte man die RS485 nicht brauchen: Programm funktioniert auch direkt am USART z.B. 
; eines FTDI FT232RL (VCP-Mode).
; 
; Details:
; - Kommunikation mit Windows Hyperterminal über RS485, dazu
; -	UART 115200 Bit/s, 8-nopar-1-off ANSI-Mode
; - UART Dialog/Menueführung
; - Steuersignalgenerierung In/Out für RS485 (RSIO-Signal)
; - ISP-Programmierbarkeit als SPI-Slave
; - SPI-Master für 6 Potentiometer MCP42xxx
; - (Chopper-Taktbereitstellung an C12, C34, C56 nur champOSv10)
; - Poti- und Clock-Daten aus EEPROM lesen
; - Poti- und Clock-Daten in EEPROM schreiben
; 
; Aufruf Hyperterminal unter WindowsXP z.B. mit
; "C:\Programme\Windows NT\hypertrm.exe"
; Bei Windows7 auf Kommandozeile eingeben: "winrs /?"
;
; Anschlüsse am ATmega8 TQFP32-Gehäuse: Nummerierung gegen UZ
; Kompatibilität TQFP32 gegen DIP28: 
; Vorsicht: Ports haben andere Pinnummern!
; 
; ATmega8 Fuses Settings (!wichtig!)
; 	BODLEVEL 	4V	 Brown.out-detection at VDD = 4V
; 	SUT_CKSEL 	Ext.crystal/resonator high freq.
; 	CKOPT 		strong: square wave output at XTAL2
;	BODEN		Brown out detection enabled
;  
; Fuses Summary:	
; 		highbyte 	0xC9
; 		lowbyte		0x3F
; 
; 
; Pinbelegungen:
;
; UART 
; 	Pin BU4		Port	U8-Pin
; 	1 	GND
; 	2 	VCC
; 	3 	RXD		PD0		30	in
; 	4 	TXD		PD1		31	out
; 	5	RSIO	PD2		32	Senden/Empfangen für RS485
; 
; SPI:	(als SPI-Slave für ISP und als SPI-Master für Potis)
; 	MOSI	PB3		Pin 15	SI an MCP42xxx
; 	MISO	PB4		Pin 16	ISP und SO über Mux U17
; 	SCK		PB5		Pin 17	SCK an MCP42xxx
; 	/RESET	PC6		Pin 29
; 	/SS		PB2		Pin 14	/CS an MCP42xxx
;
; ISP-MKII BU5: 
; 	1 GND		0V
;	2 VCC		+5V
;	3 MOSI		PB3
;	4 MISO		PB4
;	5 SCK		PB5
;	6 /RESET	PC6
; 	
; Clocks für Chopperamplifier (nur PCB Champ v10:
; 	C12		PD5		Pin 9		choppern: low	nur champOS v10
; 	C34		PD4		Pin 2		choppern: low	"
; 	C56		PD3		Pin 1		choppern: low	"
; 
; LEDs:
; 	red		PB0		Pin 12		aktiv low
; 	green	PB1		Pin 13		aktiv low
; 
; ADC:
; 	ADC0	PC0		Pin 23	K1
; 	ADC1	PC1		Pin 24	K2
; 	ADC2	PC2		Pin 25	K3
; 	ADC3	PC3		Pin 26	K4
; 	ADC4	PC4		Pin 27	K5
; 	ADC5	PC5		Pin 28	K6	(auch an PD6)
;
; 	ADC6	PB2		Pin 19	offen
;	ADC7			Pin 22	offen
; 	AREF	VCCA	Pin 20	+5V
; 	AVCC	VCCA	Pin 18	+5V
; 	
; Komparatoren:
; 	K6 		AIN0/PD6		Pin 10
; 	VREF 	AIN1/PD7		Pin 11	2,5V
; 
; "Signal":	Modifikation EEPROM Wert, wir bearbeiten einen Wert, 
; 			wenn "signal" ungleich 0
; 	signal = 0x00	default, Select Number Dialog
; 	signal = 0x10	zur Stringeingabe
; 	signal = 0x11	erstes Nibble wurde eingegeben
; 	signal = 0x12	zweites Nibble wurde eingegeben
; 	signal = 0x13	drittes Nibble wurde eingegeben (CR)
; 
; EEPROM-Adressen für Werte:
;	0x00	Nr Versuchsnummer (frei wählbar)
;	0x01-06	poti-values of channels K1...K6
;	0x07	cG poti-values (gain) beim choppern (all chls. K1-K6)	nur champOS v10
;	0x08	cP chopper period 		Takte C12,C34,C56				nur champOS v10
;	0x09	cD chopper duration 	Takte C12,C34,C56				nur champOS v10
;	0x0a	v. Version (letzte)
.equ eend = 0x0a	; letzte EEPROM-Adresse (bitte geradzahlig)

; Timer_counter1 (fuer Chopper-Interrupt)		nur champOS v10
; 	Set prescaler TCCR1B 2:0 = CS1 2:0:
; 	/1: 	001		1
; 	/8: 	010		2
; 	/64: 	011		3
; 	/256: 	100		4
; 	/1024: 	101		5
.equ timerprescal = 0x05

; lokale Register, nur innerhalb von UPs geltend
.def aa = r16
.def bb = r17
.def cc = r18
.def dd = r19

; globale Register
.def ch  = r20				; Zeichen übergeben
.def chu = r21				; UART-Zeichen übergeben
.def cmd = r22				; Command an Potis übergeben
.def nummer = r23			; EEPROM-Adresse low (bin)
.def signal = r24			; Dialog: eingegebene Zeichen
.def int_ena = r25			; Signal für Interrupt-Sperrung bei Dialog

.equ SRAM_bufflen = 0xff	; SRAM-Bufferlenght (255 Byte) 


; Quarzfrequenz in Hz, bitte Kommentarzeichen entfernen:
;.equ OSZFREQ 	= 16000000	; Quarzfrequenz in Hz
.equ OSZFREQ 	= 14745600	; Quarzfrequenz in Hz
;.equ OSZFREQ 	=  8000000	; Quarzfrequenz in Hz
;.equ OSZFREQ 	=  7372800	; Quarzfrequenz in Hz
;.equ OSZFREQ 	=  3680000	; Quarzfrequenz in Hz

; gewünschte Baudrate der UART/RS485 in Hz:
;.equ BAUD 	=   9600		; gewünschte Baudrate UART
;.equ BAUD 	=  19200		; gewünschte Baudrate UART
;.equ BAUD 	=  38400		; gewünschte Baudrate UART
;.equ BAUD 	=  57600		; gewünschte Baudrate UART
.equ BAUD 	= 115200		; gewünschte Baudrate UART
;.equ BAUD 	= 230400		; gewünschte Baudrate UART
;.equ BAUD 	= 460800		; gewünschte Baudrate UART
;.equ BAUD 	= 921600		; gewünschte Baudrate UART

; Berechnung Baudrate für gewählte OSZFREQ
; Vorsicht: kein Aufrunden! Bitte Nachrechnen!
; evtl. Rundung über ein "cast" mit -0 statt -1 korrigieren 
.equ UBRR_VAL = ((OSZFREQ)/(16*BAUD)-1)			; für UBRR-Register
.equ BAUD_REAL = (OSZFREQ/(16*(UBRR_VAL+1)))	; erreichte Baudrate
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)	; Fehler in Promille
.if ((BAUD_ERROR>10) || (BAUD_ERROR<-10)) 		; max. +/- 10 Promille ~ 1%
.warning "Fehler der Baudrate grösser ein Prozent" 
.endif

; Vorsicht! Mit internem Oszillator sind nur 
; zehn bis fünfzehn Zeichen fehlerfrei übertragbar.
; Empfehlung: externer Resonator (Murata, Kyocera u.a.)


; ########### Macro Functions #########################

; kann man, aber muß man nicht nutzen
; Vorteil: kein Ärger mit Register-Doppelbelegungen
; Nachteil: werden bei jedem Aufruf in den Code kopiert
; Variable Register werden in Macros mit @nummer bezeichnet.
; @0 ist folglich ein anderes Register als @1 etc.

.macro bin2ascii	; out/in über ein Register (@0)
					; in: Hexzahl low nibble, out: ASCII-char
					; Aufruf zB. bin2ascii r17
					; mit output r17 und input r17
	andi @0,0x0f	; Maske 0000 1111 zur Sicherheit
	subi @0,-0x30	; Addiere ASCII '0'
	cpi @0,'9'+1	; 0...9?
	brlo bin2ascend	; fertig
	; subi @0,-0x07	; Offset 7  to 'A...F'
	subi @0,-0x27	; Offset 39 to 'a...f'
	bin2ascend:
.endmacro


.macro ascii2bin	; out/in @0, Fehler setzt Carry-Flag
					; in: ASCII-char, out: Hexzahl auf low-nibble
					; Aufruf zB. ascii2bin r17
					; mit output r17 und input r17
	clc					; clear carry flag
	cpi @0,'0'			; Vergleiche mit ASCII '0'
	brlo ascii2binerr	; Fehler, ASCII ist keine Hexziffer
	cpi @0,'9'+1		; größer ASCII '9'?
	brsh ascii2binhex	; Sprung, wenn größer als 0...9
	subi @0,0x30		; ASCII '0' subtrahieren
	rjmp ascii2binrdy	; fertig

	ascii2binhex:		; liegt im hexa-Bereich a...f
	; Bereich a...f nach A...F verschieben:
	andi @0,0b11011111	; maskieren mit Bit5

	; 'A...F' decodieren:
	cpi @0,'A'			; kleiner 'A'?
	brlo ascii2binerr	; nicht im Bereich, Fehler

	cpi @0,'F'+1		; größer 'F'?
	brsh ascii2binerr	; außerhalb, Fehler
	subi @0,'A'-10		; um 0x41-0x0a=0x37 nach 0x0a bis 0x0f verschieben
	rjmp ascii2binrdy	; z.B. 'A'= 0x41 - 0x37 = 0x0a
	
	ascii2binerr: 
	sec					; Fehler: set carry flag
	ldi @0,0x00			; auf 0x00 setzen, keinen Müll erzeugen

	ascii2binrdy:		; Ausgabe Hexziffer in Form 0000_xxxx
.endmacro


.macro shifthigh	; in und out @0
					; shift low nibble to high nibble
	lsl @0			; Rest wird zu Null
	lsl @0			; um vier Bit verschieben
	lsl @0
	lsl @0			; schneller: SWAP Rd
.endmacro


.macro shiftlow		; in und out @0
					; shift high nibble to low nibble
	lsr @0			; Rest wird zu Null
	lsr @0			; um vier Bit verschieben
	lsr @0
	lsr @0
.endmacro



;#####################################
;####### jetzt gehts aber los ########
;############### Inits ###############


.cseg		;Beginn des Code-Segmentes


;########## Interrupts ###############

.org 0					;Startadresse=0
rjmp Reset      ; Reset Handler, Sprung zum Beginn                        
reti ; rjmp INT_EXT_INT0   	; IRQ0 Handler                      
reti ; rjmp INT_EXT_INT1   	; IRQ1 Handler                      
reti ; rjmp INT_TIM2_COMP  	; Timer2 Compare Handler            
reti ; rjmp INT_TIM2_OVF   	; Timer2 Overflow Handler           
reti ; rjmp INT_TIM1_CAPT  	; Timer1 Capture Handler            
rjmp INT_tim1compmatchA 	; Timer1 Compare A Handler           
reti ; rjmp INT_TIM1_COMPB 	; Timer1 Compare B Handler           
reti ; rjmp INT_INT_Timer1_Overflow   	; Timer1 Overflow Handler           	  
reti ; rjmp INT_TIM0_OVF   	; Timer0 Overflow Handler           
reti ; rjmp INT_SPI_STC    	; SPI Transfer Complete Handler 
rjmp INT_uart_receive  	; USART RX Complete Handler         
reti ; rjmp INT_USART_UDRE 	; UDR Empty Handler                 
rjmp INT_uart_txc  	; USART TX Complete UDR 6 12e Handler         
reti ; rjmp INT_ADC_HANDLER 	; ADC Conversion Complete Handler   
reti ; rjmp INT_EE_RDY     	; EEPROM Ready Handler              
reti ; rjmp INT_ANA_COMP   	; Analog Comparator Handler         
reti ; rjmp INT_TWSI       	; Two-wire Serial Interface Handler 
reti ; rjmp INT_SPM_RDY    	; Store Program Memory Ready Handler


Reset:                        
cli		; Clear Global Interrupt Flag (erstmal initialisieren)



;########## STACK initialisieren ##########
; RAMEND kommt aus der *def.inc - Datei
; start of stack pointer (Stack läuft immer rückwärts!)


ldi aa, LOW(RAMEND)      ; LOW-Byte der obersten RAM-Adresse
out SPL, aa
ldi aa, HIGH(RAMEND)     ; HIGH-Byte der obersten RAM-Adresse
out SPH, aa



;########################################
;######## Init IOs und Register #########
;########################################

Init_ios:	; (Hilfsmarke ohne Aufruf)

 Init_ascii:			; Register für UART-Kommunikation
	ldi ch,0b1010_1010	; irgenwas typisches einschreiben
	ldi chu,0b1010_1010	; irgenwas typisches einschreiben

	clr cmd				; command for MCP42 Potis: do nothing
	clr signal			; Menue - muß initial gleich null sein
	clr int_ena			; Signal für User-Dialog, Interruptsperre

 Init_LEDs:
	; red	PB0 Pin 12
	sbi ddrb,PB0	; auf Ausgabe
	;cbi portb,PB0	; einschalten

	; green PB1 Pin 13	
	sbi ddrb,PB1	; auf Ausgabe
	;cbi portb,PB1	; einschalten
	;rcall LEDred	; Rotlicht

 Init_PortC: 	; K1...K6 auf PC0...PC5 (Eingang):
	ldi	aa,0x00	; PC0...5 initialisiseren: xx00 0000 => hex 00
	out ddrc,aa	; Datenrichtung -> Eingang (0)

 Init_clocks:	; Taktausgabe:
	; 0b00111000	; PD3...5 initialisiseren: 0011 1000 = hex 38
	sbi ddrd,PD3	; PD3 auf Ausgabe ~ C56
	;sbi portd,PD3	; PD3 auf 1 ~ C56 choppern
	sbi ddrd,PD4	; PD4 auf Ausgabe ~ C34
	;sbi portd,PD4	; PD4 auf 1 ~ C34 choppern
	sbi ddrd,PD5	; PD5 auf Ausgabe ~ C12
	;sbi portd,PD5	; PD5 auf 1 ~ C12 choppern
	;rcall ChampOff		; Offsetkompensation starten

 Init_RSIO: 		; in/out Umschaltung RS485:
	sbi ddrd,PD2	; RSIO auf Ausgabe setzen: 0000 0100
	;cbi portd,PD2	; RSIO auf Low setzen: EMPFANG
	;sbi portd,2	; RSIO auf High setzen: SENDEN

 Init_UART_Send:
	; Baudrate einstellen, S.136
 	ldi aa,HIGH(UBRR_VAL)
	out UBRRH,aa
	ldi aa,LOW(UBRR_VAL)
	out UBRRL,aa
	; TX aktivieren
	sbi UCSRB,TXEN
	; Frame-Format: 8 Bit (UCSZ = 011) + 1 Stopbit (USBS = 0)
	ldi aa,(1<<URSEL)|(3<<UCSZ0)		; S.157, Tab.58 
		; URSEL selects between UBRRH and UCSRC, S.152
		; URSEL = 1: UCSRC update
		; URSEL = 0: UBRRH update
	out UCSRC,aa

;###################################
;############### Main ##############
;###################################

Main:	; (Hilfsmarke ohne Aufruf)
	
; Vorspiel
	rcall RSIO_rx		; RS485 auf EMPFANG setzen: low
	rcall ChampOff		; erstmal Offset kompensieren (nur champOSv10)
	rcall LEDred		; Rotlicht
	;rcall WatchdogSet	; Watchdog-Timer aktivieren (max. 2,1 sec)
						; reset muß mit wiederkehrendem 
						; Befehl wdr in main loop verhindert werden
						; Vorsicht, wenn Timer höher eingestellt ist!

	; Schnittstellen init
	rcall UART_RecInit			; für INT_uart_receive über Interrrupt
	rcall SPI_MasterInit 		; für SPI ohne Interrupt

	rcall EEPROM_InitCheck		; schreibt bei Erstinstallation db_data ein
	rcall EEPROM_SendVersion	; Programmversion ins EEPROM 
								; (wird nur geschrieben, wenn falsch)

	; Menue senden
	;rcall UART_SendTermInit
	rcall UART_SendNewPage
	rcall UART_SendSpace
	rcall UART_SendMsg			; Menue senden


	; lese EEPROM:
	rcall UART_SendHead
	rcall EEPROM_ReadAllValues
	rcall UART_SendSpace
	rcall UART_SendPrompt

	; Timer init
	rcall Timer_Init			; Timer-Interrupt für Choppern
	rcall Timer_int_enable		; Timer Interrupt freigeben

	sei					; Interrupts global freigeben


; Hauptprogramm
mainloop:			; hier verweilen (Label ohne Return)

			; ab jetzt passiert alles über Interrupts INT_xxx
	nop		; siehe INT_tim1compmatchA
	wdr		; watchdog reset verhindern, wenn alles OK
	nop

rjmp mainloop		; von vorn



;###################################
;############### UPs ###############
;###################################


Watchdogreset:
			; Schleife löst einen Watchdog-Reset bei Esc aus
rjmp Watchdogreset



;###################################
; Interrupt-UPs
;###################################


INT_tim1compmatchA:	; Timer Interrupt bei compare match des Timers
					; Interruptbehandlung
	cli				; Interrupts sperren
	push dd			; auf dem Stack sichern
	in dd,sreg		; SREG sichern
	push dd			; SREG gesichert

		rcall Choppern

	pop dd			; SREG holen
	out sreg,dd		; SREG wiederherstellen
	pop dd			; zurückholen
	sei				; Interrupts freigeben
reti				; Rücksprung aus Interrupt


INT_uart_receive:	; i:-   o: chu
					; UART-Empfang mit Interrupt auf Register chu
	cli				; Interrupts sperren
	push dd			; auf dem Stack sichern
	in dd,sreg		; SREG sichern
	push dd			; SREG gesichert

		in chu,UDR			; empfangenes Byte auf chu lesen,
							; dadurch wird Interrupt gelöscht
		rcall LEDtoggle		; Interrupt anzeigen
		rcall Menue			; Eingabe-Dialog abarbeiten

	pop dd			; SREG holen
	out sreg,dd		; SREG wiederherstellen
	pop dd			; zurückholen

	sbi UCSRB, RXCIE ; Interrupt für Empfang freigeben
	sei				; global Interrupts freigeben
reti				; zum vorhergehenden Programm zurückkehren


INT_uart_txc:		; wird nach Senden komplett ausgelöst
	rcall RSIO_rx	; RSIO auf Empfang schalten
	rcall LEDred
reti



;###################################
; UPs ohne Interrupt
;###################################


Reset_softstart:	; Umleitung, da zu große relative Schrittweite
rjmp Reset		; alternativ: rjmp Watchdogreset, wenn watchdog aktiviert
;rjmp Watchdogreset		; Konflikt, wenn Timer zu hoch eingestellt wird


Menue: 		; Input Register chu, o: signal
			; INT_uart_receive Interrupt-Behandlung

	rcall LEDred
	rcall Timer_int_disable	; sonst gibts Murks bei kleinen cP,cD
	ldi int_ena,0x01		; Timer Interrupt sperren

	rcall UART_SendByte	; Empfangswert zurückspiegeln (chu)
	;rcall UART_SendPrompt	; zerstört die Zeicheneingabe -> selektiv!

	; Stringeingabe zwei Zeichen hex für EEPROM

	cpi signal,0x00	; EEPROM-Wert verändern, wenn 'signal' ungleich null
				brne menue_sig	; Wert bearbeiten
				rjmp menue_nr 	; signal ist null, weiter
					menue_sig:		; signal ist nicht null
					rcall EEPROM_BurnValue ; EEPROM Wert im Dialog verändern
				menue_nr:

	cpi chu,13; send prompt after CR
				brne menue_cr
				rcall UART_SendRun
				rcall UART_SendPrompt
			rcall Timer_int_enable		; weiterarbeiten
			ldi int_ena,0		; Timer Interrupt wieder frei
				menue_cr:

	cpi chu,27	; Escape
				brne menue_esc
				rjmp Reset_softstart
				menue_esc: 

	cpi chu,'l'; clear screen
				brne menue_c
				rcall UART_SendNewPage
				rcall UART_SendPrompt
				menue_c:

	cpi chu,'h'	; help infos
				brne menue_h
				rcall UART_SendSpace
				rcall UART_SendMsg
				rcall UART_SendHelp
				menue_h: 

	cpi chu,'m'	; menue
				brne menue_m
				rcall UART_SendSpace
				rcall UART_SendMsg
				menue_m:

	cpi chu,'r'	; read EEPROM
				brne menue_r
				rcall UART_SendHead			; Kopfzeile
				rcall EEPROM_ReadAllValues	; Werte
				rcall UART_SendSpace
				rcall UART_SendPrompt
				menue_r: 

	cpi chu,'i'	; reset factory defaults, flash to EEPROM
				brne menue_i
				rcall EEPROM_WriteInitDefaults	
				rcall UART_SendPrompt
				rcall UART_SendSpace
				; read EEPROM for Verifikation
				rcall UART_SendHead			; Kopfzeile
				rcall EEPROM_ReadAllValues	; Werte
				rcall UART_SendSpace
				rcall UART_SendPrompt
				menue_i: 

	cpi chu,'s'	; select values
				brne menue_s
				rcall UART_SendSpace
				rcall UART_SendSelect
				menue_s: 

	cpi chu,'x'	; dialog burn and run
				brne menue_x
				rcall UART_SendRun
				rcall UART_SendPrompt
				rcall Timer_int_enable		; weiterarbeiten
				ldi int_ena,0		; Timer Interrupt wieder frei
				menue_x: 
	
	; 0...9,a testen:
	rcall EEPROM_GetValue	; Ziffer 0...9,a oder Unfug wurde gewählt
				
	menue_wait:
ret


EEPROM_GetValue:	; Dialog Form >1:ff , d.h. >ADR:DAT1,DAT2
	; Wert aus EEPROM lesen und an UART senden
	; i: chu, signal, o: Hexzahl in Register nummer schreiben
	; signal modifizieren
	; chu: empfangenes ASCII
	; signal: Steuerinfo
	; nummer: EEPROM YL-Adresse

	cpi signal,0x00		; ist signal für diesen Zweig, dann ist es null
	brne menue_loopend	; nein, Ende

	; Validate correct numbers 0...a
	; dazu große Schleife von 0 bis a
	ldi aa,'9'				; ASCII 9
	menue_loop:				; ASCII-loop: Ziffern 0...9,a abfragen, 
			cp chu,aa		; Vergleich mit eingekommenen Zeichen chu
			breq menue_loop3		; Treffer, unten weiter
			; wars ein anderes ASCII?

			cpi chu,'a'				; char in Apostroph setzen
			breq menue_loop3		; Treffer, unten weiter

			cpi aa,'0'-1			; ASCII-Null erreicht? Ende
			breq menue_loopend		; Schreibweise '0'-1 bedeutet ein ASCII vor '0'
			dec aa					; vermindern und nochmal
	rjmp menue_loop

	; Anzeige des EEPROM-Wertes (Form > 1:ff , d.h. ADR:DAT1,DAT2)
	; hier geht ein Wert zwischen 0 und a ein
	menue_loop3:		; Wert aus EEPROM lesen
	; Wert für EEPROM-Adresse nach bin konvertieren
		mov YL,chu			; chu auf YL (chu ist ASCII)
		ldi YH,0
		rcall Ascii2hex		; out: YH, in: YH,YL
		mov nummer,YH		; Dialognummer als bin sichern als EEPROM-Adr.low
		ldi chu,':'			; Nummer steht schon auf dem Terminal
		rcall UART_SendByte		; i: chu, o: -

	; EEPROM-Wert holen
		ldi XH,0
		mov XL,YH
		rcall EEPROM_ReadByte	; i: XH,XL, o: ch

	; EEPROM Wert (Byte) in zwei ASCII konvertieren
		mov YH,ch			; wird von Hex2ascii auf YH gebraucht
		rcall Hex2ascii		; i: YH, o: YH:YL

	; erstes Byte senden
		mov chu,YH
		rcall UART_SendByte		; i: chu

	; zweites Byte senden
		mov chu,YL
		rcall UART_SendByte		; i: chu

	; String "new value" senden
		rcall UART_SendNew
		ldi signal,0x10			; Stringeingabe nach db_new

	menue_loopend:
ret


EEPROM_BurnValue:	; Wert im EEPROM ändern		
	; i: chu, signal, nummer
	; INT_uart_receive Interrupt wird mit signal ungleich 0 hierher verbogen
	
	; signal kommt mit einem Wert 0001_xxxx herein

		inc signal		; signal wird bei jedem Durchlauf inkrementiert

		cpi signal,0x11		; erstes ASCII?
			brne eeprom_f1
			mov YH,chu		; ja, YH wird für ASCII-Konvertierung benötigt
			rjmp eeprom_nextch
			eeprom_f1:
	
		cpi signal,0x12; 	; zweites ASCII?
			brne eeprom_f2
			mov YL,chu		; ja, YL wird für ASCII-Konvertierung benötigt
			rjmp eeprom_nextch
			eeprom_f2:

		cpi signal,0x13; 	; drittes ASCII?
			brne eeprom_abbr
		cpi chu,13			; bei CR soll gebrannt werden!
			brne eeprom_abbr	; nein, Abbruch

		eeprom_brennen:
			rcall Ascii2hex	; in: YH,YL,  out: YH
			; write_eeprom
			mov ch,YH
			ldi YH,0
			mov YL,nummer			; gespeicherte Adresse
			rcall EEPROM_WriteByte	; i: YH,YL,ch

		eeprom_abbr:
			ldi signal,0x00	; signal rücksetzen, Abbruch
			rcall UART_SendSpace
			rcall UART_SendSpace
			ldi chu,'r'		; Menue r aufrufen

		eeprom_nextch:	; nächstes ASCII char empfangen
ret


EEPROM_ReadAllValues:	; 'r' send EEPROM values to UART

	ldi XH,0x00	; init EEPROM-address
	ldi XL,0x00	; init EEPROM-address

	; Schleife von 0 bis eend (siehe .def eend = ...)
	uart_nextch:

		; Byte aus EEPROM holen
		rcall EEPROM_ReadByte	; i: XH,XL, o: ch
	
		; in zwei ASCII konvertieren
		mov YH,ch			; wird von Hex2ascii auf YH gebraucht
		rcall Hex2ascii		; i: YH, o: YH:YL

		; erstes ASCII ausgeben
		mov chu,YH			; Ausgabe YH
		rcall UART_SendByte	; an UART ausgeben i: chu

		; zweites ASCII ausgeben
		mov chu,YL			; Ausgabe YL
		rcall UART_SendByte	; an UART ausgeben i: chu

		; Leerzeichen ausgeben
		ldi chu,0x20		; Ausgabe ASCII-Space
		rcall UART_SendByte	; an UART ausgeben i: chu

		cpi XL,eend			; EEPROM-Ende erreicht? 
		brge uart_endch

		adiw XH:XL,1 		; Zeiger erhöhen XL = XL+1
	rjmp uart_nextch	; nächstes Zeichen bearbeiten

	uart_endch:
ret


Hex2ascii:	; in: YH, out: YH,YL
			; input hex integer value 0x00...ff 
			; with high and low nibble
			; returns two ASCII-Bytes YH and YL
	mov YL,YH
	
	shiftlow  YH	; shift high nibble to low nibble, fill zeros
	bin2ascii YH	; convert to ASCII
	
	andi YL,0x0f	; high nibble löschen
	bin2ascii YL
	; Ausgabe je eines ASCII auf YH und YL
ret


Ascii2hex:	; out: YH, in: YH,YL
			; gets two ASCII-Bytes YH and YL
			; YH wird high nibble, YL wird low nibble
			; return hex integer value 0x00...ff

	ascii2bin YH	; inp: 8 bit, out 4 bit auf low nibble
	shifthigh YH	; auf high Nibble verschieben
					; dabei unteres Nibble löschen

	ascii2bin YL	; inp: 8 bit, out 4 bit auf low nibble
	andi YL, 0x0f	; oberes Nibble wird dabei gelöscht

	add YH,YL
ret		; fertig


SPI_MasterInit:
	
	; disable rs485 driver:
	; rcall RSIO_rx	; RS485 ausschalten

	; Chipselect der Potis aus
	sbi DDRB, PB2	; SS auf Ausgabe
    sbi PORTB,PB2	; SS=1 means not selected

	; Set MOSI and SCK output, all others input
	sbi DDRB,DDB3	; set data direction MOSI output
	sbi DDRB,DDB5	; set data direction SCK output
	cbi DDRB,DDB4	; set data direction MISO input
	sbi PORTB,PB4	; set pull-up at MISO

	; Enable SPI for Master, set clock rate fck (SPR 0,1) S.130 Tab.50 
	sbi SPCR,SPE	; SPI enable
	sbi SPCR,MSTR	; as Master
	; clock rate sel.:	/4	/16	/64	/128
	sbi SPCR,SPR1	; 	0	0	1	1
	sbi SPCR,SPR0	; 	0	1	0	1
	
	; ldi bb,(1<<SPE)|(1<<MSTR)|(1<<SPR0) ; no int, spi en, msb first, master
	; out SPCR,bb	; write to SPI Control Register
ret


SPI_SetChopperGain:	; i:-, o:-
	; set Potis to value cG

	; read EEPROM Adresse 7: cG chopper gain
	ldi XH,0x00	; chopper gain address high
	ldi XL,0x07	; chopper gain address low
	rcall EEPROM_ReadByte		; i: XH, XL, o: ch
								; read from EEPROM to ch

	; Potis MCP42 auf Anschlag setzen
	ldi cmd,0x13	; write to both potis (1: write, 3: both)

	; senden
	rcall SPI_PotiSelect	; i:- o:-
							; MCP42 Chipselect über /SS=0, PB2

		rcall SPI_SendCmd		; send byte cmd
		rcall SPI_SendChar		; send byte ch	an K5,K6
		rcall SPI_SendCmd		; send byte cmd
		rcall SPI_SendChar		; send byte ch	an K3,K4
		rcall SPI_SendCmd		; send byte cmd
		rcall SPI_SendChar		; send byte ch	an K1,K2

	rcall SPI_PotiUnsel		; i:- o:-
							; MCP42 Chipselect rücksetzen /SS=1
ret



SPI_SetAllChls:	; i:-, o:-
	; set potis to values K1...K6
					; SPI transmission of commands and data
					; for six MCP42xxx potis

	rcall SPI_PotiSelect		; MCP42 chip select über /SS=0

	; nur die richtige Reihenfolge bestimmt die Ankunft!
	; letztes Poti (K5, K6) zuerst senden

	; MCP42 command byte (register cmd):
	;	0b_xxxx_xx10	für Poti mit Pins 5,6,7		K1,3,5
	;	0b_xxxx_xx01 	für Poti mit Pins 8,9,10	K2,4,6
	;	0b_xxxx_xx11	both potis (for chopper)
	;	0b_xx01_xxxx	write data
	;	0b_xx10_xxxx	shutdown

	; Wert 0x00 bedeutet 0 Ohm zwischen Pins 5-6 bzw.9-10

	ldi XH,0x00		; EEPROM adress high byte; XL:XH

	; load potis with pins 5,6,7
		ldi cmd,0x12	; 0b0001_0010

		; U12-Poti_1	K5
		rcall SPI_SendCmd		; sends cmd
		ldi XL,5				; load EEPROM-address lowbyte
		rcall EEPROM_ReadByte	; write to ch
		rcall SPI_SendChar		; sends ch

		; U8-Poti_1		K3
		rcall SPI_SendCmd
		ldi XL,3
		rcall EEPROM_ReadByte
		rcall SPI_SendChar

		; U4-Poti_1		K1
		rcall SPI_SendCmd
		ldi XL,1
		rcall EEPROM_ReadByte
		rcall SPI_SendChar

	rcall SPI_PotiUnsel		; MCP42 Chipselect rücksetzen /SS=1
			nop
			nop
			nop				; damit /CS-Wechsel zu sehen ist
			nop
			nop
	rcall SPI_PotiSelect	; MCP42 chip select über /SS=0

	; load resistors with pins 10,9,8
		ldi cmd,0x11

		; U12-Poti_0	K6
		rcall SPI_SendCmd
		ldi XL,6
		rcall EEPROM_ReadByte
		rcall SPI_SendChar

		; U8-Poti_0		K4
		rcall SPI_SendCmd
		ldi XL,4
		rcall EEPROM_ReadByte
		rcall SPI_SendChar

		; U4-Poti_0		K2
		rcall SPI_SendCmd
		ldi XL,2
		rcall EEPROM_ReadByte
		rcall SPI_SendChar

	rcall SPI_PotiUnsel		; MCP42 Chipselect rücksetzen /SS=1
ret


Choppern:		; Choppern entfällt ab Champ6v20
	rcall SPI_SetAllChls	; Potis auf K1...K6 setzen
	rcall LEDgreen
ret


Choppern_v10:	; Preamp choppern
		; Etappen
		; - Multiplexer (Mux) umschalten C12, C34, C56 -> low
		; - Potis setzen
		; - Pause
		; - Potis rücksetzen
		; - Mux rückschalten C12, C34, C56 -> high

	rcall Timer_int_disable	; Interrupt während Pause sperren

	; choppern:		
		; entfällt bei PCB Champ v20
		rcall SPI_SetChopperGain	; alle Potis auf cG setzen
		rcall ChampOff				; Offsetregelung
		rcall ChopPause				; Choppern braucht Zeit

	; rücksetzen:
	rcall SPI_SetAllChls	; Potis auf K1...K6 setzen
	rcall ChampOn			; auf Normalbetrieb schalten

	rcall Timer_Init		; Timer-Interrupt aktualisieren
							; Timer-Interrupt enable durch Menue_x
	cpi int_ena,1			; Timerinterrupt bleibt gesperrt
		breq choppernwei		; wenn Zeicheneingabe läuft
		rcall Timer_int_enable
	choppernwei:
	; fertig:
ret


SPI_PotiSelect:			; i:- o:-
	cbi portb,PB2		; MCP42 chip select /CS = /SS = 0
ret


SPI_PotiUnsel:			; i:- o:-
	sbi portb,PB2		; MCP42 chip select reset /SS = 1
ret


SPI_SendCmd:	; input: cmd (Byte)
	out SPDR,cmd	
	; Wait for transmission complete
	spi_wait1:
	sbis SPSR,SPIF	; Skip if Bit in I/O Register is Set
					; auf Interrupt Flag warten
	rjmp spi_wait1
ret


SPI_SendChar:	; input: ch (Byte)
	out SPDR,ch	
	; Wait for transmission complete
	spi_wait2:
	sbis SPSR,SPIF	; Skip if Bit in I/O Register is Set
					; auf Interrupt Flag warten
	rjmp spi_wait2
ret


SPI_SlaveInit:	; Vorsicht! hier nicht, Kurzschluss?

	; Set MISO output, all others input
	ldi bb,(1<<DDB4)	; DD_MISO als Ausgang
	out DDRB,bb		; DDR_SPI

	; Enable SPI
	ldi bb,(1<<SPE)
	out SPCR,bb	; write to SPI Control Register
ret



SPI_SlaveReceive: ; (dd) ; Vorsicht! hier nicht, Kurzschluss?

	; Wait for reception complete
	sbis SPSR,SPIF
	rjmp SPI_SlaveReceive

	; Read received data and return
	in dd,SPDR	; get dd from SPI Data Register
ret



WatchdogSet:
; Watchdog aktivieren für Zeit 2,1 sec
	ldi aa,0b00011000  ; 1.mal (wichtig)
	out wdtcr,aa       
	ldi aa,0b00001111  ; 2.mal (wichtig)
	out wdtcr,aa		; Soft-Reset
ret


Timer_Init:		; Init Timer-Interrupt

	; read chopper period from EEPROM
		ldi XH,0x00	; EEPROM address for chopper period
		ldi XL,0x08	; EEPROM address for chopper period
		rcall EEPROM_ReadByte	; i: XH,XL, o: ch
								; Wert wird auf ch gelesen

	; Set CTC-mode:
	; (clear timer on compare match) S.88 
	; -> OCF1A-Interrupt benutzen, nicht TOIE1 siehe Tab.18, S.46
	; $006 rjmp INT_tim1compmatchA ; Timer1 CompareA Handler

		ldi cc,0x00			; reset WGM11 und WGM10
		out TCCR1A,cc		; siehe S.98 Tab.39 + S.100 oben

		ldi cc,0x08			; reset WGM13, set WGM12, S.100 oben
		out TCCR1B,cc		; siehe auch Simulator I/O-view

	; Set OCR1AH			; in CTC-mode 
		out OCR1AH,ch		; set output compare reg.
		; out TCNT1H,ch		; set timer (each cycle!)

	; Set OCR1AL			; 0xff um Interrupt-Mismatch zu vermeiden
		ldi aa,0xff			; zu schnelle Folge verhindern
		out OCR1AL,aa		; set output compare reg.
		; out TCNT1L,ch		; set timer (each cycle!)

	; Set prescaler TCCR1B 2:0 = CS1 2:0
		; 	/1: 	001		1
		; 	/8: 	010		2
		; 	/64: 	011		3
		; 	/256: 	100		4
		; 	/1024: 	101		5
		in cc,TCCR1B
		ori cc, timerprescal	; set prescaler
		out TCCR1B,cc

	; Set TOIE1 overflow interrupt enable (hier nicht)
			; sbi TIMSK,TOIE1	; geht nicht
			; sbi TIMSK,2		; geht nicht
			; ldi dd, 0x04		; geht, aber überschreibt Reg.
		;	in dd,TIMSK			; geht
		;	ori dd,1<<TOIE1		; geht
		;	out TIMSK,dd		; geht

	; Set output compare matchA interrupt enable:
	; separat durchführen mit rcall Timer_int_enable
ret


Timer_int_enable:	; Set output compare matchA interrupt enable
	in dd,TIMSK
	ori dd,1<<OCIE1A
	out TIMSK,dd
ret


Timer_int_disable:
	in dd,TIMSK
	andi dd,~(1<<OCIE1A)
	out TIMSK,dd
ret


RSIO_tx:		; RS485 TXD
	; rcall LEDred
	sbi portd,PD2	; RSIO auf High setzen: RS485 auf TX
ret


RSIO_rx:		; RS485 RXD
	cbi portd,PD2	; RSIO auf Low setzen: RS485 auf RX
	; rcall LEDgreen
ret


LEDtoggle:			; i:-, o:-
	; toggle LEDs red = /PB0 <-> green = /PB1
	sbis portb,PB0	; skip if bit PB0 is set (red light)
	rjmp ledwasnotset
	cbi portb,PB0	; rücksetzen
	sbi portb,PB1	; setzen
	rjmp ledendtog
	ledwasnotset:
	sbi portb,PB0	; setzen
	cbi portb,PB1	; rücksetzen
	ledendtog:
ret


LEDred:			; rot ein, grün aus
	cbi portb,PB0	; LED red PB0 setzen, leuchtet bei 0
	sbi portb,PB1	; LED green PB1 rücksetzen, leuchtet bei 0
ret


LEDgreen:		; rot aus, grün ein
	sbi portb,PB0	; LED red PB0 rücksetzen, leuchtet bei 0
	cbi portb,PB1	; LED green PB1 setzen, leuchtet bei 0
ret


EEPROM_WriteInitDefaults:	; Daten vom Flash db_data zum EEPROM schieben
	; ohne Parameter
	; nach Inbetriebnahme Aufruf deaktivieren!
	; kopiert nur Init-Daten in den EEPRPOM-Bereich

	ldi ZH,high(db_data*2)	; Flash-Startadresse
	ldi ZL,low(db_data*2)	; *2: LSBit ausblenden

	ldi YH,0				; EEPROM-Startadresse
	ldi YL,0

	eeprom_loop:
	lpm ch,Z				; nächstes Byte ind. mit Z-Reg. laden

	rcall EEPROM_WriteByte	; i: YH,YL,ch

	adiw ZH:ZL,1 			; Zeiger erhöhen Z
	adiw YH:YL,1 			; Zeiger erhöhen Y

	cpi YL,eend+1 			; Stringende erreicht? 
	brne eeprom_loop		; nächstes Zeichen bearbeiten
ret


EEPROM_WriteByte:	; i: YH,YL,ch
	; Adresse: YL low, YH high, Daten r0
	; Wait for completion of previous write
	cli 	; disable interrupts during timed sequence
	sbic EECR,EEWE
	rjmp EEPROM_WriteByte
	; Set address (YH:YL) in EEPROM address register
	out EEARH, YH
	out EEARL, YL
	; Write byte r0 to data register
	out EEDR,ch
	; Write logical one to EEMWE
	sbi EECR,EEMWE
	; Start eeprom write by setting EEWE
	sbi EECR,EEWE
	sei 	; enable interrupts 
ret


EEPROM_ReadByte:	; i: XH,XL, o: ch
	; Adresse: XL low, XH high, Daten ch
	; Wait for completion of previous write
	cli 	; disable interrupts during timed sequence
	sbic EECR,EEWE
	rjmp EEPROM_ReadByte
	; Set up address (XH:XL) in address register
	out EEARH, XH
	out EEARL, XL
	; Start eeprom read by writing EERE
	sbi EECR,EERE
	; Read data from data register
	in ch,EEDR
	sei 	; enable interrupts 
ret


EEPROM_InitCheck:	; prüft, ob Wert pgmversion = 0xff
					; falls ja, werden Init-Werte aus db_data gebrannt
					; wenn nein, werden bestehende Werte erhalten
	; read pgmversion from EEPROM
	ldi XL,0x0a				; Pgmversion steht auf Adresse 0x0a
	rcall EEPROM_ReadByte	; Inhalt von "0x0a" auf ch lesen
	cpi ch,0xff				; nur schreiben, wenn gleich
	brne eepromch
		rcall EEPROM_WriteInitDefaults	; überschreibt alle Werte
	eepromch:
ret


EEPROM_SendVersion:		; i:- o:-	Programmversion ins EEPROM schreiben

	; read pgmversion from EEPROM
	ldi XL,0x0a				; Pgmversion steht auf hex "0a"
	rcall EEPROM_ReadByte	; Inhalt von "0a" auf ch lesen
	cpi ch,pgmversion		; nur schreiben, wenn verschieden
	breq eeprweiter

	; write pgmversion to EEPROM
	ldi ch,pgmversion
	ldi YH,0x00
	ldi YL,0x0a		; EEPROM Adresse 10
	rcall EEPROM_WriteByte	; i: YH,YL,ch

	eeprweiter:
ret


ChampOn:	; Muxer auf Normalbetrieb
	; Takte C56 = PD3; C34 = PD4; C12 = PD5
	; xx11 1xxx	; PD3...5
	
	sbi PORTD,PD3
	sbi PORTD,PD4
	sbi PORTD,PD5

	rcall LEDgreen
ret


ChampOff:	; Mux auf Offsetkompensation setzen
	; Takte C56 = PD3; C34 = PD4; C12 = PD5
	; xx00 0xxx	; PD3...5

	cbi PORTD,PD3
	cbi PORTD,PD4
	cbi PORTD,PD5

	rcall LEDred
ret


ChopPause:	; i:-  o:-
			; max. 2^16 Takte, Rückwärtszähler
	push aa
	push bb
	push cc

	; get duration value from EEPROM
	ldi XH,0x00	; default
	ldi XL,0x09	; EEPROM address for duration
	rcall EEPROM_ReadByte	; 	i: XH,XL, o: ch
	
	cpi ch,0
	brne chop_pausew	
	subi ch,-1	; 00-Eingabe verhindern, eins addieren
	
	chop_pausew:
	mov aa,ch	; Wert-Übergabe an aa
	chopsync1:
		mov bb,ch	; Wert-Übergabe an bb
		chopsync2:
				ldi cc,0x0f	; Grundpause 16 Takte
				chopsync3:
				dec cc
				brne chopsync3
		dec bb
		brne chopsync2
	dec aa
	brne chopsync1

	pop cc
	pop bb
	pop aa
ret


UART_RecInit:	; UART Init Receive mit Interruptfreigabe
	sbi UCSRB, RXEN		; RX (Empfang) aktivieren
	sbi UCSRB, RXCIE	; Interrupt bei Empfang freigeben
ret


UART_SendSelect:	; 
	ldi ZH,high(db_select*2)	; 
	ldi ZL,low(db_select*2)		; 
	rcall UART_serout
ret	


UART_SendHelp:	; 
	ldi ZH,high(db_help*2)		; 
	ldi ZL,low(db_help*2)		; 
	rcall UART_serout
ret


UART_SendNewPage:
	ldi chu,12
	rcall UART_SendByte
ret


UART_SendPrompt:
	ldi ZH,high(db_prompt*2)	; 
	ldi ZL,low(db_prompt*2)		; 
	rcall UART_serout
ret


UART_SendSpace:
	ldi ZH,high(db_space*2)		; 
	ldi ZL,low(db_space*2)		; 
	rcall UART_serout
ret

UART_SendHead:
	ldi ZH,high(db_headline*2)		; 
	ldi ZL,low(db_headline*2)		; 
	rcall UART_serout
ret


UART_SendNew:
	ldi ZH,high(db_new*2)		; 
	ldi ZL,low(db_new*2)		; 
	rcall UART_serout
ret


UART_SendRun:
	ldi ZH,high(db_run*2)		; 
	ldi ZL,low(db_run*2)		; 
	rcall UART_serout
ret


UART_SendMsg:
	; Ausgabe eines Strings aus dem Flash
	; z.B. UART Eröffnungstext an PC senden
	; Text an UART senden
	ldi ZL,low(db_menue*2)	; Z Pointer laden (Reg 30 + 31) für LPM
	ldi ZH,high(db_menue*2)	; in Wortorganisation *2 !!!
	rcall UART_serout
ret


UART_SendTermInit:		; UART_Init_ANSI_Terminal
	ldi ZH,high(db_TerminalSet*2)		; 
	ldi ZL,low(db_TerminalSet*2)		; 
	rcall UART_serout
ret

;########### UART-Sendemethoden #############


UART_SendByte:			; send one ASCII from register chu to UART
	rcall RSIO_tx		; wird im TXComplete Interrupt rückgesetzt
	sbi UCSRB, TXCIE	; Interrupt für Senden freigeben
	; cbi UCSRA,TXC		; clear TX bit in I/O-reg. (nicht nötig bei Interrupt)
	sbis UCSRA,UDRE		; Warten bis UDR für das nächste Byte bereit ist
	rjmp UART_SendByte
	out UDR,chu			; Zeichen von Reg. chu ausgeben
	uart_rdy:
	; sbis UCSRA,TXC	; skip if bit is set (end of transmission)
	; rjmp uart_rdy		; fertig abwarten für RSIO
	; rcall RSIO_rx		; auf INT_uart_txc Interrupt verlagert
	sbi UCSRB, RXCIE	; Interrupt für Empfang freigeben
ret


UART_serout:	; String an UART senden, Bytes nacheinander
				; inp: Adresse in ZH,ZL, outp: string an UART
				; (LPM wird in Wortorganisation *2 vorausgesetzt)
	rcall RSIO_tx		; wird im TXComplete Interrupt rückgesetzt
	sbi UCSRB, TXCIE	; Interrupt für Senden freigeben
	; cbi UCSRA,TXC		; clear TX bit in I/O-reg. (nicht nötig bei Interrupt)

	lpm 				; nächstes Byte aus Z-Reg. auf r0 laden
	; LPM loads one byte pointed to by the Z register to register r0

	and r0,r0 			; Stringende erreicht? (String ist Null-terminiert)
	breq uart_outend 	; wenn gleich, -> Ende

	uart_outwait:
	sbis UCSRA,UDRE 	; Warten, bis UDR bereit ist
	rjmp uart_outwait
	out UDR, r0			; Ausgabe des Zeichens auf UDR
	adiw ZH:ZL,1 		; Zeiger erhöhen ZL+1

	rjmp UART_serout	; nächstes Zeichen bearbeiten

	uart_outend:
	; sbis UCSRA,TXC	; skip if bit is set (end of transmission)
	; rjmp uart_outend	; fertig abwarten für RSIO
	; rcall RSIO_rx		; auf INT_uart_txc Interrupt verlagert
	sbi UCSRB, RXCIE	; Interrupt für Empfang freigeben
ret


;########### SRAM read write #############


SRAM_Test:

	ldi YH,high(SRAM_reserve)		;
	ldi YL,low(SRAM_reserve)		;

ret



;#############################################
;############### Daten im SRAM ###############
;#############################################
.dseg

.org SRAM_START		; Vorgabewert, beim ATmega8 ab 0x0060 siehe m8def.inc
					; Vorsicht: vom RAMEND kommt der Stack entgegen!

SRAM_reserve: .byte SRAM_bufflen	; SRAM_bufflen Bytes reservieren



;############################################
;############### Daten im Flash #############
;############################################
.cseg				; Codesegment (Flash)

; Message-String definieren und speichern
; sollte am Programmende stehen, sonst Müsli
;
; ASCII: 
; 7: Bell, 12: Form feed, 10: Line feed, 
; 13: Carriage return, 0: Null (end of file)
;
; Achtung: Flash ist in 2-Byte Organisation (ZL + ZH)
; Zeichenzahl muss gerade sein, sonst "padding a zero byte"


db_data:	; Reset auf Werkseinstellungen
	; EEPROM-Erstinitialisierung, Test mit EEPROM_WriteInitDefaults
	; Überschreibt alle Werte(!)  Liste siehe db_select
.db 			0x00, 0xff,\
				0xff, 0xff,\
				0xff, 0xff,\
				0xff, 0xab,\
				0xcd, 0xef,\
				0x20, 0

db_menue:	
	.db 12,13,\
	"6-Channel Photo Amplifier Operating System",10,13,\
	"ChampOS v21 12/2010 src:champosv20.asm",10,13,\
	"board CHAMP6v20  heinz@gfai.de",10,13,\
	"Hyperterminal ANSI 115200 Baud 8-N-1-N ",10,13,10,13,\
	"Select:",10,13,\
	" ESC: softstart-reset",10,13,\
	" i: init - reset to factory defaults ",10,13,\
	" h: help",10,13,\
	" m: main menue",10,13,\
	" r: read and write parameters",10,13,\
	" s: show more about parameters",10,13,\
	" x: (or second return) burn and start working",10,13,\
	10,13,">",0

db_help:
	.db 13,\
	"Communication:",10,13,\
	" Board Champ6v20 Bu2, Pins 3-6 over RS485-USB-converter to PC",10,13,\
	" or to welding machine",10,13,\
	10,13,"Installation (winXP):",10,13,\
	" Install USB-connection (Virtual COM-Port, VCP):",10,13,\
	" - download VCP-driver winXP_setup_VCP_CDM 2.04.06.exe",10,13,\
	"   from http://www.ftdichip.com/Drivers/VCP.htm",10,13,\
	" - install VCP-driver",10,13,\
	" - connect with USB2SERIAL converter >115200 Baud ",10,13,\
	" Start Windows Hyperterminal",10,13,\
	" - file/new ",10,13,\
	" - set to: ANSI-terminal, 115200 Baud, 8 data bits,",10,13,\
	" - no parity, 1 stop bit, no stream control",10,13,\
	" Use Windows Device-Manager to find COM-portnumber",10,13,\
	" - set COM-portnumber",10,13,\
	" - save settings as *.ht",10,13,\
	" Use file/print to print this help-text ",10,13,\
	10,13,"Technical descriptions: (circuits, source code):",10,13,\
	" see http://www.gfai.de/~heinz/techdocs/index.htm",10,13,\
	10,13,"Hotline:",10,13,\
	" GFaI, Dr.G.Heinz ",10,13,\
	" Volmerstr.3 ",10,13,\
	" 12489 Berlin",10,13,\
	" heinz@gfai.de",10,13,\
	" Tel. +49 30 814563-490",10,13,\
	10,13,">",0

db_headline_chopper:
	.db 13,\
	"Parameters:",10,13,\
	"  0  1  2  3  4  5  6  7  8  9  a",10,13,\
	" Nr K1 K2 K3 K4 K5 K6 cG cP cD Vn",10,13,\
	" ",0

db_headline:
	.db 13,\
	"Select:",10,13,\
	"  0  1  2  3  4  5  6  7  8  9  a",10,13,\
	" Nr K1 K2 K3 K4 K5 K6 xx xx xx Vn",10,13,\
	" ",0

db_headline_next:
	.db 13,\
	"Select:",10,13,\
	"  0  1  2  3  4  5  6  7  8  9  a",10,13,\
	" Nr K1 K2 K3 K4 K5 K6 Uo Ua Ss Vn",10,13,\
	" ",0

db_prompt:		; Prompt nach Ausführung
	.db 13,10,">",0

db_space:		; Commando-Prompt (vor Ausführung)
	.db 13,10," ",0

db_new: .db	10,13," (hex 00...ff, return-key to finish): ",0,0

db_run:	.db	13,"> ... LED green ... ",0

db_select_chopper:		; champOS v10
	.db 12,13,\
	"Select:",10,13,\
	" 0: Nr number of experiment (user) ",10,13,\
	" 1: K1 gain",10,13,\
	" 2: K2 gain",10,13,\
	" 3: K3 gain",10,13,\
	" 4: K4 gain",10,13,\
	" 5: K5 gain",10,13,\
	" 6: K6 gain",10,13,\
	" 7: cG chopper gain (all channels)",10,13,\
	" 8: cP chopper period",10,13,\
	" 9: cD chopper duration",10,13,\
	" a: Vn current programm version ",10,13,\
	"Finish with x (execute)",10,13,\
	10,">",0

db_select:				; champOS v20
	.db 12,13,\
	"Select:",10,13,\
	" 0: Nr number of experiment (user) ",10,13,\
	" 1: K1 gain",10,13,\
	" 2: K2 gain",10,13,\
	" 3: K3 gain",10,13,\
	" 4: K4 gain",10,13,\
	" 5: K5 gain",10,13,\
	" 6: K6 gain",10,13,\
	" 7: xx",10,13,\
	" 8: xx",10,13,\
	" 9: xx",10,13,\
	" a: Vn current programm version ",10,13,\
	"Finish with x (execute) to burn EEPROM ",10,13,\
	10,">",0

db_select_next:
	.db 12,13,\
	"Select:",10,13,\
	" 0: Nr number of experiment (user) ",10,13,\
	" 1: K1 gain",10,13,\
	" 2: K2 gain",10,13,\
	" 3: K3 gain",10,13,\
	" 4: K4 gain",10,13,\
	" 5: K5 gain",10,13,\
	" 6: K6 gain",10,13,\
	" 7: Uo puls stop threshold",10,13,\
	" 8: Ua puls start threshold",10,13,\
	" 9: Ss send control (hex 00: off)",10,13,\
	" a: Vn current programm version ",10,13,\
	"Finish with x (execute)",10,13,\
	10,">",0

db_TerminalSet:	; Escape-Terminaleinstellung (ANSI-Mode)
	; funktionieren beim Windows-Hyperterminal nur rudimentär
	; "Esc[0;34;47m" plain text,blue on white
	; .db 27,'[','0',';','3','0',';','4','7','m',0,0
	; normal
	; .db 27,"[0;34;47 m",0
	; .db 27,"[0;30;47m",0,0
	; ESC [ ? 5 l 	; weis auf schwarz
	; .db 27,"[?51",0
	; .db 27,91,63,53,49,0
	; irgendwas anderes: ESC [ m
	; .db 27,91,109,0
	; double-width char
	; .db 27,"#6",0
	; dann eben nur ein CR und ein LF:
	.db 13,10," ",0

; ANSI-Terminal Escape-Sequences: 
; (Funktionieren beim Hyperterminal kaum)
				; Form: Esc [ P1; P2;...;PN m
;
; Text display: Example: The sequence ’ESC[1;4m’ will
; select bold underlined text.
				; 0 Plain text
				; 1 Bold text
				; 4 Underlined text
;
; Text Colors: For instance, the sequence ‘ESC[4;31;40m’ will select 
; underlined red text on black background. 
;
				; Text colour	Background colour
				; 30 Black 		40 Black
				; 31 Red 		41 Red
				; 32 Green 		42 Green
				; 33 Yellow 	43 Yellow
				; 34 Blue 		44 Blue
				; 35 Magenta 	45 Magenta
				; 36 Cyan 		46 Cyan
				; 37 White 		47 White



