;***************************************************************************
;*	Steinhart Winfried	1.3.2000	16.05.2007
;*
;*	SELECTRIX-Lokdecoder Stationär am SX-Bus
;*
;*
;*	Drehzahlregelung DC-Motor mit modifizierter PWM und EMK-Rückführung.
;*
;*	Regelprinzip:
;*	Vergleich der Gegen-EMK des Motors mit einer Vorgabespannung.
;*	Weil nur 1 Schwelle (des 1 Komparator) vorhanden ist, mit einer
;*	fixen Hysterese von <10 mV, sind nur die beiden Reaktionen
;*	Beschleunigen und Bremsen möglich.
;*	Die EMK wird bei unbestromtem Motor erfasst. Wegen der Abklingzeit
;*	der (negativen) Induktionsspannung ist eine Wartezeit notwendig,
;*	die vom Motor abhängig ist. Der Testmotor hat eine Ind. von 5 mH
;*	und einen Widerstand von ca 25 Ohm, d.h. Tau = 0.005/25 = 0.2 mSek.
;*	Die Abklingzeit wird auf ca 0.6 ms eingestellt.
;*	Als Abtastzeit wird 0.4 mSek verwendet. Ausgang PORTD,6 steuert
;*	den Analogschalter im HEF4066. Die EMK lädt bei geschlossenem
;*	Schalter über 4k7 1 MikroF. Die so geglättete EMK ist mit AIN-
;*	verbunden. Die Sollspannung liegt an AIN+.
;*
;*	Die Gesamt-Mindest-Pausenzeit ist: Entladezeit (576 Mikrosek) +
;*	Samplezeit (224 Mikrosek) = 800 Mikrosek.
;*	Um auf volle Motorspannung von 12 V zu kommen muss der Max-Ein
;*	-Impuls	entsprechend der Versorgungsspannung ermittelt werden:
;*	T-EIN = 12/(18V-12V) * 800 = 1600 Mikrosek.
;*
;*	Der Motor-EIN-Impuls ist zwischen 12 und 100 Zeitticks lang.
;*	ein Zeittick ist bei 4MHZ und Prescale 64 16 Mikrosek.lang.
;*	Bei Last-Sprung wird der Motorimpuls pro Zyklus um 1 Tick verlängert
;*	(dec r17) d.h. erst nach max 88 Zyklen wird max Leistung erreicht.
;*	Bei einer durchschnittlichen Zykluszeit von ca 1.6 mSek erst nach
;*	ca 140 Millisekunden.
;*	Überschwingen der Geschwindigkeit ist (fast) ausgeschlossen, weil
;*	die Pausenverlängerung unmittelbar wirksam wird, d.h. die Endstufe
;*	ist abgeschaltet, solange die Sollspg. überschritten ist.

;*******	letzte Änderung 11.05.07
;*		
;*************************************************************************** 

;* Target MCU           :ATtiny2313
;*
;* Taktleitung PORTD, PIN PD3 (7), Datenleitung PORTD Pin PD0 (2)
;* Taktfrequenz 8 MHZ
;*************************************************************************** 





;***************  Anwendungswerte eintragen ! ******************************
;
;* Kalibrierwert eintragen wie angezeigt (Advanced) für 8 Mhz Clock
;* Fuse """" INTERNAL CLOCK DIVIDE NICHT PROGRAMMIEREN """" ***************

.SET Leskal = 0x50


;***************  Adressliste *********************************************
;
; Die Adresse bestimmt die Gruppennummer und die Unteradresse UA

; Gru= 15, 14, 13, 12, 11, 10, 09, 08, 07, 06, 05, 04, 03, 02, 01, 00

; UA6= 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15
; UA5= 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
; UA4= 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47
; UA3= 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63
; UA2= 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79
; UA1= 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95
; UA0= 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111
;**************************************************************************

.SET Gru = 15
		; Aus Liste für die Adresse entnommen (z.B. Adr. 64)

;**************************************************************************
;* Das Lokdatenwort wird aus jeweils 2 Registern für 1 Unteradresse
;* gebildet
;**************************************************************************

;	bitregLow:				bitregHi:				für: UaGru = 0

;	r2						r3						UA 0
;	r5 -------------------- r6 --------------------	UA 2
;	r8						r9						UA 4
;	r11 ------------------- r12 ------------------- UA 6

;	bitregLow				bitregHI:				für: UaGru = 1

;	r4						r5						UA 1		
;	r7 -------------------- r8 -------------------- UA 3
;	r10						r11						UA 5

;***************************************************************************


.DEF bitregLow 	= r5		
.DEF bitregHi 	= r6
.SET UagGru		= 0


;*  Nach Build und Programmieren MIT SX-Bus-Signal testen.
;*
;***** A C H T U N G !!!   SX-Bus zum Programmieren A B S T E C K E N !! ***
;*
;***************************************************************************


.include "tn2313def.inc"

.CSEG

;***** Programm-Code					**
;**************************************************************************

	rjmp	RESET		;Reset Handle
	rjmp	EXT_INT0
	rjmp	EXT_INT1
	rjmp	TIM_CAPT1
	rjmp	TIM_COMP1
	rjmp	TIM_OVF1
	rjmp	TIM_OVF0
	rjmp	UART_RXC
	rjmp	UART_DRE
	rjmp	UART_TXC
	rjmp	ANA_COMP


;**	Geschwindigkeitstabelle im Programm-Memory	**
;** sp(n) = 100 EXP 1/32 Exp n x 10				**
;**************************************************************************
speed:
	.DW 0,	 10, 14, 16, 18, 21, 24, 28, 33, 38
	.DW  44, 51, 59, 69, 79, 92,107,124,144,167
	.DW 194,225,261,302,351,407,472,548,635,737,855, 992
	.DW 1023, 1023


.CSEG

;**	Hier gehts los					**
;**************************************************************************

RESET:
	ldi 	r20,Leskal		; gelesener Kalibrier-Wert für 1.6 Mhz Clk
	out 	OSCCAL, r20


	ldi		r17,low(RAMEND)	; Programm-Startadresse laden
	out		SPL,r17			; Stackpointer LO
	
	wdr						; Watchdog Reset
	ldi		r17,0x8			; Watchdog enable WDE
	out		WDTCR,r17
	
	ldi		r17,0x3			;*** Takteingang PORT D Bit PD2 ***
	out		MCUCR,r17		; ISC00 mit ISC01 für INT0 raising edge
	

	ldi		r17,0x02
	out		TIMSK,r17	; Timer 0 enable
		
	ldi		r17,0xF8	; Bit 3,4,5,6,7 auf Ausgang
	out		DDRB,r17	; setzen.

	ldi		r17,0x70	; Bit 4,5,6 auf Ausgang
	out		DDRD,r17	; setzen.

	cbi		PORTD,6		; Analogschalter AUF = Lo.

	cbi		PORTB,7		; Endstufe AUS = VorLoSide = Lo.
	cbi		PORTB	,6		; Endstufe AUS = VorHiSide = Lo.
	cbi		PORTB,5		; Endstufe AUS = RückLoSide = Lo.
	cbi		PORTB,4		; Endstufe AUS = RückHiSide = Lo.	

	ldi		r17,0x03
	out		TCCR0,r17	; CK/64

	ldi		r17,0x83	; 10 Bit PWM, OC1 not inverted PWM
	out		TCCR1A,r17	; Timer1 Controlregister 1A

	clr 	r17
	out		OCR1AH,r17	; PWM Vergleichswert HI
	ldi		r17,20
	out		OCR1AL,r17	; PWM Vergleichswert HI

	ldi		r17,0x01	; Timer1 mit clk/1 starten,0.5mSek
	out		TCCR1B,r17	; Timer1 Controlregister 1B

	ldi		r17,0x40
	out		GIMSK,r17	; enable INT10

	ldi		r23,230		; Startwert Pausenzeit
	ldi		r24,230		; Startwert Einschaltzeit
	out		TCNT0,r23

	sei					; enable Int. im Statusregister
	

;**	Warteschleife					**
;***************************************************************************
forever:
	wdr					; Watchdog Reset00000
	rjmp	forever


;**	Interruptservice-Routinen			**
;***************************************************************************

EXT_INT1:	reti
TIM_CAPT1:	reti
TIM_COMP1:	reti
TIM_OVF1:	reti
UART_RXC:	reti
UART_DRE:	reti
UART_TXC:	reti
ANA_COMP:	reti


;**	Interruptservice für 8-Bit-Timer		**
;***************************************************************************
TIM_OVF0:
	push	r26
	in		r26,SREG
	push	r26

	tst		r19			; wenn Fahrstufe = 0, Endstufen HiSide AUS
	brne	fahr

	cbi		PORTB,6		; VorHiSide AUS
	cbi 	PORTB,4		; RückHiSide sollt schon AUS sein !
	rjmp 	exbesch1	; Wartezeit starten


fahr:	
	sbis	PORTB,6		; wenn VorHiSide EIN, Ein-Impls beenden
	rjmp	rueck		; Endstufe ist AUS

	cbi		PORTB,6		; VorHiSide AUS
	cbi 	PORTB,4		; RückHiSide sollte schon AUS sein !
	rjmp 	x0			; Entladezeit starten

rueck:
	sbis	PORTB,4		; wenn RueckHiSide EIN, Ein-Imouls beenden
	rjmp	x1			; beide HiSide Aus, Entladezeit fertig

	cbi		PORTB,4		; RueckHiSide AUS
	cbi		PORTB,6		; VorHiSide sollte schon aus sein

;**	Nach EIN-Impuls Entladezeit starten		**
;***************************************************************************

x0:
	ldi		r23,206		; (256-206)*64/8 = 0.400ms
	out		TCNT0,r23

	pop		r26
	out		SREG,r26
	pop		r26
	reti				; Entladezeit läuft
	
;**	Nach Entladezeit Sample-Zeit starten		**
;***************************************************************************
x1:	sbic	PORTD,6		; wenn Endst. AUS UND Analogsch. AUF
	rjmp	x2			; Schalter geschlossen, zum Geschwindigkeitstest
	
	sbi		PORTD,6		; Analogschalter ZU

	ldi		r23,206		; Samplezeit mindestens 0.400ms
	out		TCNT0,r23

	pop		r26
	out		SREG,r26
	pop		r26
	reti				; Sample-Zeit läuft

;**	Zum Bremsen Sample-Zeit verlängern und EIN-Impuls verkürzen **
;***************************************************************************
brems:
	clr		r18
	cpi		r24,245		; min-EIN = (256-231)*64/8 = 200
	brsh	x4
	inc		r24			; Impulszeit kürzer
	 
x4:	ldi		r23,221		; Samplezeit verlängern um 200 Mikrosek.
	out		TCNT0,r23

	pop		r26
	out		SREG,r26
	pop		r26
	reti				; Sample Verlängerung läuft

;**	Nach Sample-Zeit Spannungsvergleich machen	**
;***************************************************************************

x2:	sbis	ACSR,ACO	; skip bei SOLL>IST auf besch.
	rjmp	brems		; Samplezeit verlängern


;**	Zum Beschleunigen EIN-Impuls verlängern		**
;***************************************************************************

besch:
	cpi		r24,56		; max-EIN = 200*64/8 = 1600 Mikrosek.
	brlo	x3
	dec		r24			; länger EIN-schalten
	
x3:	cbi		PORTD,6		; Analogschalter AUF, vor Ein-Impuls
	nop
	nop

;**	Fahrtrichtung prüfen und HiSide schalten		**
;***************************************************************************

	sbis	PORTB,7		; wenn VorLoSide EIN Vorwärts
	rjmp	ruelo

	cbi		PORTB,5		; RueckLoSide sollte schon AUS sein
	sbi		PORTB,6		; VorHiSide EIN
	out		TCNT0,r24	; EIN-Impuls-Zeit
	rjmp	exbesch

ruelo:
	sbis	PORTB,5		; wenn keine LoSide eingeschaltet
	rjmp	exbesch1	; zum Wartezeit setzen

	sbi		PORTB,4		; RueckHiSide EIN
	out		TCNT0,r24	; EIN-Impuls-Zeit

exbesch:
	pop		r26
	out		SREG,r26
	pop		r26
	reti				; Einschaltzeit läuft
	
exbesch1:				; NICHTS eingeschaltet  !
	ldi		r23,236		; Nach 160 Mikrosek. nächste EIN-Prüfung
	out		TCNT0,r23
	pop		r26
	out		SREG,r26
	pop		r26
	reti				; Wartezeit läuft

;**	******  Ende Regler-Teil  *******
;***************************************************************************


;***************************************************************************
;*	Dieser Entry ist der Interrupt nach steigender Taktflanke.
;*	Der TRIX-Takt hat 50 Mikrosek. Zykluszeit, d.h.während dem Scan der
;*	insgesamt 96 Bits für eine Basisadress-Information muss jedes
;*	Datenbit erkannt werden.
;*	Die Daten werden von Bit = r12 nach Links in die niedrigeren
;*	Register geschoben. (Rotate left through Carry)
;*	In diesem Testprogramm ist PORT D PIN D0 der Dateneingang.
;*	Die Schiebebefehle bringen eine Verzögerungszeit bis zum Lesen
;*	der Datenleitung. Damit ist sichergestellt, dass der Input mit
;*	dem "richtigen" Status gelesen wird.

;*	Diese Interrupt-Service-Routine braucht ca. 40 Takte incl.
;*	Annahmezeit und Return wenn kein Syncsignal vorliegt.

EXT_INT0:			;Interrupt-Service-Routine

;**	Software Störunterdrückung 12 Befehle HI-Signal	**

	sbis	PIND,2		; Pin 2, PortD ist INT0-Eingang
	reti
	sbis	PIND,2
	reti
	sbis	PIND,2
	reti
	sbis	PIND,2
	reti
	sbis	PIND,2
	reti
	sbis	PIND,2
	reti
	sbis	PIND,2
	reti
	sbis	PIND,2
	reti
	sbis	PIND,2
	reti
	sbis	PIND,2
	reti
	sbis	PIND,2
	reti
	sbis	PIND,2
	reti

		
;**	Schieberegister für 1 Synch-Block + Vorlaufregister	**


	lsl	r13		; Vorlaufregister für Syncvergleich
	rol	r12		; Start für 96 Bit Schieberegister
	rol	r11
	rol	r10
	rol	r9		; Schieberegister through carry
	rol	r8
	rol	r7
	rol	r6
	rol	r5
	rol	r4
	rol	r3
	rol	r2
	rol	r1		; Synchrongruppe im Hi-Nibble,
				; Adresse im Lo-Nibble



;**	Datenleitung übernehmen					**

	clr		r25			; Bit 0 auf Datum LO stellen
	sbic	PIND,0		; Skip wenn Datenleitung LO
	
	ldi		r25,0x01	; Setze LSB weil Datenleitung HI
	or		r13,r25		; Eintragen in Vorlaufregister


	
;***************************************************************************
;*	Nach jedem Eintrag wird geprüft ob die 5 Synchronbits da sind.
;*	Nach TRIX SX-Codierung 0-0-0-1-x-1-B3-B2-1-B1-B0-1 in den obersten 12 Bit
;*	x ist das Gleisspannungsbit. B0 bis B3 ist die Basisadresse
;*
;*
;*	AVR-Micros erlauben nur in den oberen 16 Registern Immediate
;*	Befehle (andi, cpi usw.)
;***************************************************************************


	mov		r16,r1			; alte Synchronbits holen und testen
	andi	r16,0xF4		; die oberen 6 Bit gelten, 1-1-1-1-0-1-0-0
	cpi		r16,0x14		; 0-0-0-1-0-1-0-0 = Sync
	breq	sync1

	reti

sync1:

	mov		r16,r13			; neue Synchronbits im Vorlaufregister prüfen
	andi	r16,0xF4		; die oberen 6 Bit gelten, bit 0,1,3 auf 0
	cpi		r16,0x14		; Bit 5,6,7 Lo UND 4,2 Lo = Sync
	breq	sync			; beide Bitmuster gleich, Telegrammlänge ok
							; Daten in den Registern fertig

	reti

;***************************************************************************
;*	Wenn die Synchronbits komplett sind wird die Basisadresse geprüft.
;*	Die Prüfung ist in die Interruptloop eingeschlossen, d.h. reti muss
;*	ausgeführt sein damit das nächste Datenbit noch richtig erkannt wird.
;*	Der Interrupt wird abgeschaltet wenn Adresse stimmt.
;*	Die Adressdecodierung verlängert die Int.Service um ca 12 Takte auf
;*	ca 45 Takte.
;*	Die Prozessorfrequenz muss sicherstellen, dass das folgende Datenbit
;*	im nächsten Takt noch sicher erkannt wird.
;***************************************************************************


sync:

	mov		r16,r1		; 6 Bit sync + 2 Bit Adresse, B3-B2
	andi	r16,0x03	; sync-Bits löschen
	lsl 	r16
	lsl 	r16
	
	mov		r17,r2		; Adress- und Datenbyte 1-B1-B0-1-D7-D6-1-D5
	andi	r17,0x60	; Datenbits löschen		0-B1-B0-0-0-0-0-0
	swap 	r17			;						0-0-0-0-0-B1-B0-0
	lsr 	r17			;						0-0-0-0-0-0-B1-B0
	or 		r16,r17		; Adresse fertig r16 =	0-0-0-0-B3-B2-B1-B0


	cpi		r16,Gru		; **** Adresstest *********************

	breq	daten		; Adresse gültig, Daten extrahieren


	reti				; falsche Basis-Adresse, weiterscannen

daten:
	sbi		PORTD,5		; *** Triggerbit Adresse gültig !!

	ldi 	r22,UagGru	; ******** Anwender gesetzt ********

	cpi		r22,1		; Unteradressgruppe 2 ?
	breq	gruppe2

;***************************************************************************

;.DEF bitregLow = r11		; Aus Liste z.B. für UA 6 und Datenbit D0
							; D0=3 D1=2 D2=0
;.DEF bitregHi = r12		; D3=7 D4=5 D5=4 D6=2 D7=1

	clr 	r20				; Ergebnisregister
	sbrc 	bitregLow,3
	sbr 	r20,1			; V1 
	sbrc 	bitregLow,2
	sbr 	r20,2			; V2
	sbrc 	bitregLow,0
	sbr 	r20,4			; V4 

	sbrc 	bitregHi,7
	sbr 	r20,8			; V8
	sbrc 	bitregHi,5
	sbr 	r20,16			; V16
	sbrc 	bitregHi,4
	sbr 	r20,32			; Richtung
	sbrc 	bitregHi,2
	sbr 	r20,64			; Licht
	sbrc 	bitregHi,1
	sbr 	r20,128			; Horn

	rjmp	fertig			; Lokdatenwort in r20 fertig

	

gruppe2:

;.DEF bitregLow = r10		; Aus Liste z.B. für UA 6 und Datenbit D0
							; D0=7 D1=6 D2=4 D3=3 D4=1 D5=0
;.DEF bitregHi = r11		; D6=6 D7=5

	clr 	r20				; Ergebnisregister
	sbrc 	bitregLow,7
	sbr 	r20,1			; V1 
	sbrc 	bitregLow,6
	sbr 	r20,2			; V2
	sbrc 	bitregLow,4
	sbr 	r20,4			; V4 
	sbrc 	bitregLow,3
	sbr 	r20,8			; V8
	sbrc 	bitregLow,1
	sbr 	r20,16			; V16 
	sbrc 	bitregLow,0
	sbr 	r20,32			; Richtung

	sbrc 	bitregHi,6
	sbr 	r20,64			; Licht
	sbrc	bitregHi,5
	sbr 	r20,128			; Horn

							; Lokdatenwort in r20 fertig

;****************************************************************

fertig:

	cbi		PORTD,PIND4		;*** TEST_AUSGABE *********************
	sbrc 	r20,2			;Skip wenn Bit LO
	sbi		PORTD,PIND4		;Datum Spiegeln für Osci-vergleich

	mov 	r19,r20			; Lokwort kopieren
	andi 	r19,0x1F		; Fahrstufe stehenlassen

	sbrc	r20,7			; Sofort Stop Mit Funktionstaste HORN
	clr 	r19				; Fahrstufe 0 setzen

	mov		r28,r19
	lsl		r28				; *2 wegen Wortabstand im P-Memory
	clr		r29				; = Offset in speed-Tabelle

;**	Richtungsbit auswerten, LoSide Einschalten		**

	sbrc	r20,5			; Richtungsbit 0 = Vorwärts
	rjmp	rue
	cbi		PORTB,5			; RückLoSide AUS
	cbi		PORTB,4			; RückHiSide AUS
	nop
	nop
	sbi		PORTB,7			; VorLoSide  EIN
	rjmp	sp

rue:
	cbi		PORTB,7			; VorLoSide AUS
	cbi		PORTB,6			; VorHiSide AUS
	nop
	nop
	sbi		PORTB,5			; RückLoSide EIN

;**	Geschwindigkeitswert im PWM setzen	**

sp:	ldi		ZL,low(speed*2)	; Pointer auf Geschw.Tabelle
	ldi		ZH,high(speed*2)
	add		ZL,r28			; Fahrstufe x 2
	inc		ZL
	lpm						; speedwert high in r0 laden
	out		OCR1AH,r0		; PWM Vergleichswert HI
	dec 	ZL				; dann Low
	lpm						; speedwert low in ro
	out		OCR1AL,r0		; PWM Vergleichswert HI

	cbi		PORTD,5			;*** Triggerbit Adresse gültig !!!
	
	reti

;***************************************************************************

	.exit
		
</html>