 	list p=16f628
;**************************************************************
;*  	Pinbelegung
;*	----------------------------------	
;*	PORTA:	0 LED------------------------------------------------A
;*		1 LED--------------------------------A
;*		2 LED----------------A
;*		3 Segment D
;*		4 -
;*		5 -
;*		6 - 
;*		7 - 	
;*
;*	PORTB:	0 Segment A	   AAAAA	   AAAAA	   AAAAA
;*		1 Segment F	  F     B	  F     B	  F     B
;*		2 Segment E	  F     B	  F     B	  F     B
;*		3 Pulseingang	   GGGGG  	   GGGGG  	   GGGGG 
;*		4 Segment H	  E     C	  E     C	  E     C
;*		5 Segment C	  E     C	  E     C	  E     C
;*		6 Segment G	   DDDDD  HH	   DDDDD  HH	   DDDDD  HH
;*		7 Segment B    
;*	
;**************************************************************
;
;sprut (zero) Bredendiek 01/2004
;
; Drehzahlmesser mit 16F628 und Fototransistor
;
; Prozessor 16F628 o.ä.
;
; Prozessor-Takt 12 MHz
;
; Meßimpulseingang RB3
;
;**************************************************************
; Includedatei für den 16F628 einbinden

	#include <P16f628.INC>

	ERRORLEVEL      -302    	;SUPPRESS BANK SELECTION MESSAGES

; Configuration festlegen:
; Power on Timer, kein Watchdog, HS-Oscillator, kein Brown out, kein LV-programming
	__CONFIG	_PWRTE_ON & _WDT_OFF & _HS_OSC & _BODEN_OFF & _LVP_OFF


;**************************************************************
; Variablen festlegen

w_copy		equ	0x20	; nur für INT
s_copy		equ	0x21	; nur für INT
p_copy		equ	0x22	; nur für INT

Flags		equ	0x23
Fehler		equ	0x25	; Fehlerregister für Mathematik

; Dezimalstellen
Digit		equ	0x29	; aktuell angezeigte Stelle
Ziffer1		equ	0x2A	; Hunderter
Ziffer2		equ	0x2B	; Zehner
Ziffer3		equ	0x2C	; Einer
HdH		equ	0x2D	;3  Hunderter
HdZ		equ	0x2E	;2  Zehner
HdE		equ	0x2F	;1  Einer
HdX		equ	0x30	; Puffer für eine Dezimalstelle

;16 Bit Rechenregister
f0		equ	0x31	; 
f1		equ	0x32	; 
f2		equ	0x33	; 
;16 Bit Rechenregister
xw0		equ	0x34	; 
xw1		equ	0x35	;
xw2		equ	0x36	;  
;16 Bit Rechenregister
g0		equ	0x37	; 
g1		equ	0x38	; 
g2		equ	0x39
; Meßwert wird hier gespeichert
PeriodeL	equ	0x3A
PeriodeH	equ	0x3B

; Konstanten festlegen
#define Zehner		Flags,7	; die Anzeige erfolgt mit 10Hz-Auflösung
#define	Datenda		Flags,6	; neuer Meßwert wurde gemessen
#define	Kommaan		Flags,5	; Dezimalpunkt ein, da neue Messung fertig

Ini_opt		equ	B'10000100'	; Timer0 int 32:1, pull-up on

;**************************************************************
	org	0
	goto	Init


;**************************************************************
; die Interuptserviceroutine

	org 	4 	
intvec
	movwf	w_copy		; w retten
	swapf	STATUS, w 	; STATUS retten
	clrf	STATUS
	movwf	s_copy		;
	movf	PCLATH, W
	movwf	p_copy
	clrf	PCLATH		; Bank 0 

; Intrupt servic routine
	clrf	TMR1L		; Timer 1 zurücksetzen
	clrf	TMR1H
	bcf	PIR1,CCP1IF	; Interupt-Flag löschen

	btfss	PIR1,TMR1IF	; Timer1 übergelaufen?
	goto	Int_copy	; nein, weitermachen
				; ja: Drehzahl zu niedrig, dann Anzeige ' --'
	movlw	D'10'
	movwf	Ziffer1		; 1. Stelle dunkel
	movlw	D'11'
	movwf	Ziffer2
	movwf	Ziffer3		; Anzeige ' --'
	goto	Int_end

Int_copy
	; Meßwert retten
	movfw	CCPR1L
	movwf	PeriodeL
	movfw	CCPR1H
	movwf	PeriodeH
	bsf	Datenda		; neuen Meßwert melden
	bsf	Kommaan		; Dezimalpunkt einschalten

Int_end
	bcf	PIR1,TMR1IF	; Timer1 Überlaufflag löschen
	bcf	PIR1,CCP1IF	; Interupt-Flag löschen
	movf	p_copy, W
	movwf	PCLATH
	swapf	s_copy, w	; STATUS zurück
	movwf	STATUS 
	swapf	w_copy, f	; w zurück mit flags
	swapf	w_copy, w

	retfie

;**************************************************************
; 7-Segment-Tabelle

;*	PORTB:	0 Segment A	   AAAAA
;*		1 Segment F	  F     B
;*		2 Segment E	  F     B
;*		3 Segment D	   GGGGG
;*		4 Segment H	  E     C
;*		5 Segment C	  E     C
;*		6 Segment G	   DDDDD  HH
;*		7 Segment B    
;
Segmente
	addwf	PCL, f
;		  76543210
; 		  BGCHDEFA
	retlw	B'01010000'	; 0
	retlw	B'01011111'	; 1
	retlw	B'00110010'	; 2
	retlw	B'00010110'	; 3
	retlw	B'00011101'	; 4
	retlw	B'10010100'	; 5
	retlw	B'10010000'	; 6
	retlw	B'01011110'	; 7
	retlw	B'00010000'	; 8
	retlw	B'00010100'	; 9
	retlw	B'11111111'	; dunkel
	retlw	B'10111111'	; -

;**************************************************************
; Routine zum multiplexen Anzeigen am LED-Display
Display
	bsf	PORTA, 2	; Ziffer1 aus
	bsf	PORTA, 1	; Ziffer2 aus
	bsf	PORTA, 0	; Ziffer3 aus

	bsf	PORTA,3		; Segmente aus
	movlw	B'11110111'
	iorwf	PORTB,f	

	decf	Digit,f		; Ziffernzähler verringern

;anstehende Arbeit:
;Digit=3: falls neuer Meßwert da ist: Rechnen
;Digit=2: anzeigen Ziffer 3    1er
;Digit=1: anzeigen Ziffer 2   10er
;Digit=0: anzeigen Ziffer 1  100er

	btfsc	STATUS, Z
	goto	Disp_0		; Z-Flag=1 ergo Digit=0
	decf	Digit, w
	btfsc	STATUS, Z
	goto	Disp_1		; Digit=1
	btfss	Digit,0
	goto	Disp_2		; Digit=2
	goto	Disp_3		; Digit=3

Disp_0
	movlw	4
	movwf	Digit		; Digit wieder auf 4 einstellen
	movfw	Ziffer1		; Wert der 1. Ziffer (100er)
	btfss	STATUS,Z	; führende '0' wird nicht angezeigt!
	bcf	PORTA, 2	; 1. Ziffer einschalten
	btfsc	Zehner		; 10 Hz Auflösung?
	bcf	PORTA, 2	; ja: 1. doch Ziffer einschalten
	goto	Disp_end

Disp_1
	movfw	Ziffer2		; Wert der 2. Ziffer (10er)
	bcf	PORTA, 1	; 2. Ziffer einschalten
	goto	Disp_end

Disp_2
	movfw	Ziffer3		; Wert der 3. Ziffer (1er)
	bcf	PORTA, 0	; 3. Ziffer einschalten
	goto	Disp_end

Disp_3
	btfsc	Datenda		; ist neuer Meßwert da?
	call	Berechnung	; ja: Drehzahl berechnen
	goto	Disp_end3	; keine Segmente einschalten

Disp_end
	call	Segmente	; 7-Segment-Code für aktuelle Ziffer holen
	movwf	PORTB		; Segmente einschalten
	bsf	PORTA,3	
	iorlw	B'11110111'	; Bit3 erhalten, rest=1
	andwf	PORTA,f		; Bit3 nach RA3

	; Dezimalpunkt nur nach Messung ein
	btfss	Kommaan
	goto	Disp_end2
	;Komma anschalten nach 2. Stelle
	decf	Digit, w
	btfsc	STATUS, Z	; 2. Stelle aktiv?	
	bcf	PORTB,4		; ja: Dezimalpunkt an
	bcf	Kommaan		; Dez.p. aus bis zur nächsten Messung

Disp_end2
	btfss	Zehner		; 10 Hz Auflösung?
	goto	Disp_end3	; nein
	btfss	Digit,2		; 1. Stelle angezeigt?
	goto	Disp_end3	; nein
	bcf	PORTB,4		; ja: Dezimalpunkt an

Disp_end3
	return

;**************************************************************
; Das Programm beginnt mit der Initialisierung

Init	movlw	B'11111111'
	movwf	PORTB		; LEDs aus
	bsf     STATUS, RP0	; Bank 1
	movlw   Ini_opt     	; pull-up on; Timer0 32:1 interner Takt
	movwf   OPTION_REG 
	movlw	B'00001000'	; PortB alle outputs außer RB3
	movwf	TRISB
	movlw	B'11110000'	; PortRA0..3 outputs 
	movwf	TRISA
	bcf     STATUS, RP0	; Bank 0
	movlw	B'11111111'
	movwf	PORTB		; LEDs aus
	
; 16F628 alle Comparatoreingänge auf Digital umschalten
	BSF	CMCON, CM0
	BSF	CMCON, CM1
	BSF	CMCON, CM2

	clrf	INTCON      	; Interupt disable  
	clrf	Flags		; Programm-Flags löschen

; Anzeige initialisieren
	movlw	4
	movwf	Digit
	movlw	D'11'
	movwf	Ziffer1
	movwf	Ziffer2
	movwf	Ziffer3		; Anzeige '---'

; Timer0 initialisieren für LED-Multiplex-Anzeige
; 32:1 Vorteiler. 
; Immer nach 256 x 32 Zyklen (2,72 ms) läuft der Timer0 über
	bsf     STATUS, RP0	; auf Bank 1 umschalten
	movlw	B'10000100'	; internen Takt zählen, Vorteiler zum Timer0, 32:1
	movwf	OPTION_REG
	bcf     STATUS, RP0	; auf Bank 0 zurückschalten
	clrf	TMR0
	bcf	INTCON, T0IE	; Timer0 interupt verbieten
	bsf	INTCON, GIE	; Interupt erlauben
	bcf	INTCON, T0IF	; Flag löschen

; Timer1 auf 2,666µs-Takt einstellen für Messung
; interner Takt, Vorteiler 8:1
; 12MHz / 4 / 8 = 375 kHz
	movlw	B'00110001'	; internen Takt zählen, Vorteiler 8:1
	movwf	T1CON
; Capture Mode
	movlw	B'00000110'	; Capture mode, jede 4. steigende Flanke
	movwf	CCP1CON

; als Lebenszeichen auf der hintersten Stelle ein Punkt
	bcf	PORTB,4		; Dezimalpunkt
	bcf	PORTA, 0	; 3. Ziffer einschalten	

; Messung erst starten, wenn 2x Capturen erfolgte
; damit der 1. Meßwert schon richtig ist
	clrf	TMR1L
	clrf	TMR1H
	bcf	PIR1,CCP1IF	
sync1	
	btfss	PIR1,CCP1IF
	goto	sync1
	clrf	TMR1L
	clrf	TMR1H
	bcf	PIR1,CCP1IF
	bcf	PIR1,TMR1IF

	; int erlauben
	bsf     STATUS, RP0	; auf Bank 1 umschalten
	clrf	PIE1
	bsf	PIE1,CCP1IE 
	bcf     STATUS, RP0	; auf Bank 0
	bcf	Datenda
	clrf	INTCON
	bsf	INTCON,PEIE
	bsf	INTCON,GIE
	
; Anzeigeschleife
disploop
	btfss	INTCON, T0IF
	goto	disploop
	bcf	INTCON, T0IF
	call	Display
	goto	disploop


;**************************************************************
; Neuberechnung des Anzeigewerts
Berechnung
	; Meßwert in xw laden
	bcf	INTCON,GIE
	movfw	PeriodeL
	movwf	xw0
	movfw	PeriodeH
	movwf	xw1
	clrf	xw2
	bsf	INTCON,GIE

	; falls Drehzahl zu hoch, dann Anzeige '---'
	movlw	D'11'
	movwf	Ziffer1
	movwf	Ziffer2
	movwf	Ziffer3		; Anzeige '---'
	; bei Meßwerten<512 abbrechen (Fehlmessung oder >87 890 rpm)
	movfw	xw1
	btfsc	STATUS, Z
	goto	BerLoop		; <256
	decf	xw1,w
	btfsc	STATUS, Z
	goto	BerLoop		; <512

	; Drehzahl ist nicht zu hoch, Berechnung kann beginnen
Ber1
	; 450 000 durch Meßwert teilen
	; 450000 = 06 DD D0
	; 450000 nach f laden
	movlw	0x06
	movwf	f2
	movlw	0xDD
	movwf	f1
	movlw	0xD0
	movwf	f0

	; Test für 10 Hz oder 100 Hz-Auflösung
	bcf	Zehner
	movlw	B'11100000'
	andwf	xw1,w
	btfsc	STATUS,Z	; xw>31 ?
	goto	Ber100		; nein, weiter mit 100Hz Auflösung
	bsf	Zehner		; ja: 10er Auflösung
	; 4 500 000 durch Meßwert teilen
	; 4 500 000 = 44 AA 20
	; 4 500 000 nach f laden
	movlw	0x44
	movwf	f2
	movlw	0xAA
	movwf	f1
	movlw	0x20
	movwf	f0	

Ber100
	; zum besseren Runden zu den 450000(0) den halben Meßwert addieren
	clrc
	rlf	f0,f
	rlf	f1,f
	rlf	f2,f
	call	Add24		; f:= 450000(0)*2 + Meßwert
	clrc
	rrf	f2,f
	rrf	f1,f
	rrf	f0,f		; f:= 450000(0) + Meßwert/2
	
	; nun durch den Meßwert dividieren
	call	Div24		; f:= f / xw

	; Divisionsergebnis in BCD wandeln & anzeigen (3-stellig)
	call	OutDez16

	; wieder in den Timer0-Rythmus synchronisieren, 
	; da ansonsten letze Displaystelle dunkler erscheint
	bcf	INTCON, T0IF
BerLoop
	btfss	INTCON, T0IF
	goto	BerLoop
	bcf	INTCON, T0IF
	bcf	Datenda		; Berechnung des Meßwertes erledigt
	return

;**************************************************************
;+++Mathematik-Routinen ***************************************

;**************************************************************
; 24 Bit Subtraktion, bei Überlauf (neg. Ergebnis) ist C gesetzt
Sub24				; 24 bit f:=f-xw   calc=xw cnt=f
	clrf	Fehler		; extraflags löschen

	movf    xw0, w		; f0=f0-xw0
	subwf   f0, f

	btfsc   STATUS,C
	goto    sb0
	movlw   0x01		; borgen von f1
	subwf   f1, f

	btfsc   STATUS,C
	goto    sb0
	subwf   f2    ,f        ; borgen von f2

	btfss   STATUS,C
	bsf	Fehler, C	; unterlauf

sb0     movf    xw1, w		; f1=f1-xw1
	subwf   f1, f

	btfsc   STATUS,C
	goto    sb1
	movlw   0x01		; borgen von f2
	subwf   f2    ,f

        btfss   STATUS,C
	bsf	Fehler, C	; Unterlauf

sb1     movf    xw2,w		; f2=f2-xw2
        subwf   f2, f

        btfss   STATUS,C
       	bsf	Fehler, C	; Unterlauf

	bcf	STATUS, C
	btfsc	Fehler, C
	bsf	STATUS, C
	return

;**************************************************************
;24 bit Adition, C-Flag bei Überlauf gesetzt
Add24 				; 24-bit add: f = f + xw
	movf	xw0,w		; low byte
	addwf	f0,f 		; low byte add

	movf	xw1,w 		; next byte
	btfsc	STATUS,C 	; skip to simple add if C was reset
	incfsz	xw1,w 		; add C if it was set
	addwf	f1,f 		; high byte add if NZ
	
	movf	xw2,w		; high byte 
	btfsc	STATUS,C	; überspringe falls C nicht gesetzt 
	incfsz	xw2,w		; addiere C falls gesetzt 
	addwf	f2,f		; next byte add wenn  NZ 

	return 			; fertig


;**************************************************************
;primitive 24 bit Division 	f:= f / xw
; die Langsamkeit dieser Routine verursacht Displayflackern bei hohen Drehzahlen
Div24
	clrf	g0
	decf	g0, f
	clrf	g1
	decf	g1, f
	clrf	g2
	decf	g2, f
Div24Loop
	incf	g0, f
	btfsc	STATUS, Z
	incf	g1, f
	btfsc	STATUS, Z
	incf	g2, f
	call	Sub24		;
	btfss	STATUS, C	;Überlauf
	goto	Div24Loop	;Stelle 1 mehr
	movfw	g0
	movwf	f0
	movfw	g1
	movwf	f1
	movfw	g2
	movwf	f2
	return


;**************************************************************
; positiven 16 Bit Wert (f1,f0) dezimal anzeigen
OutDez16			;16-bit (f0,f1) als Dez zum Lcd
	call	Hex2Dez16	; 16-bit(f1,f0) in 3-stellen Bcd (H,Z,E)
	; EINT TEST AUF <1000 EINFÜGEN!!
	movfw	HdH
	movwf	Ziffer1
	movfw	HdZ
	movwf	Ziffer2
	movfw	HdE
	movwf	Ziffer3
	return

;              100 = 0000 0064 h
;               10 = 0000 000A h
;                1 = 0000 0001 h

Hex2Dez16			; 16-bit(f1,f0) in 3-stellen Bcd (H,Z,E)	
	clrf	f2
	
	movlw	0x00		; 100 = 00 00 64 h
	movwf	xw2
	movwf	xw1
	movlw	0x64
	movwf	xw0

	clrf	HdX
	decf	HdX, f
HdLoop3
	incf	HdX, f
	call	Sub24		;
	btfss	STATUS, C	;Überlauf
	goto	HdLoop3		;Stelle 1 mehr
	call	Add24

	movfw	HdX
	movwf	HdH

	movlw	0x00		; 10 = 00 00 0A h
	movwf	xw2
	movwf	xw1
	movlw	0x0A
	movwf	xw0

	clrf	HdX
	decf	HdX, f
HdLoop4
	incf	HdX, f
	call	Sub24		;
	btfss	STATUS, C	;Überlauf
	goto	HdLoop4		;Stelle 1 mehr
	call	Add24

	movfw	HdX
	movwf	HdZ

	movfw	f0
	movwf	HdE
	return


	end
