	list p=16f876
;**************************************************************
;*  	Pinbelegung
;*	----------------------------------	
;*	PORTA: 	0 < Spannung U
;*		1 -
;*		2 -
;*		3 -
;*		4 -
;*		5 -
;*		6 -
;*		7 -
;*
;*	PORTB:	0 LCD Display E
;*		1 -
;*		2 LCD Display RS
;*		3 LCD Display R/W
;*		4 LCD Display D4
;*		5 LCD Display D5
;*		6 LCD Display D6
;*		7 LCD Display D7
;*	
;*	PORTC:	0 -
;*		1 -
;*		2 -
;*		3 -
;*		4 -
;*		5 -
;*		6 -    
;*		7 -
;*	
;**************************************************************
;
;sprut (zero) Bredendiek 01/2003
;
; Lehrbeispiel: ADC mit LCD
;
; Prozessor 16F876 
;
; Prozessor-Takt 10 MHz
;
; LCD am PortB
;**********************************************************
; Includedatei für den 16F876 einbinden

	#include <P16f876.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

;16 Bit Rechenregister
xw0			equ	0x22	; 
xw1			equ	0x23	; 
f0			equ	0x24	; 
f1			equ	0x25	; 
counter		equ	0x26	; 
Fehler		equ	0x27	; 
ST			equ	0x28	; 
SZ			equ	0x29	; 
SH			equ	0x2A	; 
SE			equ	0x2B	; 
loops		equ	0x2C	; timer für wait
loops2		equ	0x2D	; timer für wait
LcdStatus	equ	0x2E	;
LcdDaten	equ	0x2F	;

pause1s1	equ 0x19	;
pause1s2	equ 0x20	; 
pause1s3	equ 0x21	;

Ini_con		Equ	B'00000000'	; TMR0 -> Interupt disable
Ini_opt		Equ	B'00000010'	; pull-up

; für LCD-Pins
#define	LcdE		PORTB,0		; enable Lcd
#define	LcdRw		PORTB,3		; read Lcd
#define	LcdRs		PORTB,2		; Daten Lcd (nicht control)	
#define LcdPort		PORTB		; Datenbus des LCD (obere 4 Bit)

;********************************************************
; Das Programm beginnt mit der Initialisierung

Init	bsf     STATUS, RP0	; Bank 1
	movlw   Ini_opt     	; pull-up on
	movwf   OPTION_REG 
	movlw	B'00000000'	; PortB alle outputs 
	movwf	TRISB
	bcf     STATUS, RP0	; Bank 0
	clrf	PORTB
	movlw   Ini_con     	; Interupt disable
	movwf   INTCON   

; ADC initialisieren
	; ADC einschalten
	BSF	ADCON0, 0	; ADON=1
	; ADC speed für 5 ... 20 MHz einstellen
	BSF	ADCON0, 7	; ADCS1=1
	BCF	ADCON0, 6	; ADCS0=0


;Display initialisieren
	call	InitLcd

Mainloop
	call 	porta0		; data z AD vstupu A0
	call	ADC			; Namerene napeti na f1,f0
	call	mV			; Zmena na mV podle f1,f0
	call	B2D			; Konverze do destitkove souistavy ST,SH,SH,SE
	call	Ausgabe1	; Zobrazeni na LCD

	call 	porta1		; data z AD vstupup A1
	call	ADC			; Namerene napeti na f1,f0
	call	mV			; Zmena na mV podle f1, f0
	call	B2D			; Konverze do destitkove souistavy ST,SH,SH,SE
	call	Ausgabe2	; Zobrazeni na LCD

	call	pause1s		; pauza 1s na dalsi mereni

	goto	Mainloop


;*****************************************************
; AD prevodnik na portu A0
porta0
	; Vyber AD vstupu podle AN0
	BCF	ADCON0, 5	; ADCHS2=0
	BCF	ADCON0, 4	; ADCHS1=0
	BCF	ADCON0, 3	; ADCHS0=0
	; Daten rechtsbündig
	BSF	STATUS,RP0	; Bank1
	clrf	ADCON1
	BSF	ADCON1, 7	; ADFM=1
	BCF	STATUS,RP0	; Bank0
	return

;*****************************************************
; AD prevodnik na portu A1
porta1
	; ADC-Eingang AN0 auswählen
	BCF	ADCON0, 5	; ADCHS2=0
	BCF	ADCON0, 4	; ADCHS1=0
	BSF	ADCON0, 3	; ADCHS0=0
	; Daten rechtsbündig
	BSF	STATUS,RP0	; Bank1
	clrf	ADCON1
	BSF	ADCON1, 7	; ADFM=1
	BCF	STATUS,RP0	; Bank0
	return

;*****************************************************
; Spannung mit ADC messen
; Ergebnis nach F1,f0
ADC
	BSF	ADCON0, 2	; ADC starten
ADCloop
	BTFSC	ADCON0, 2	; ist der ADC fertig?
	GOTO	ADCloop		; nein, weiter warten
	movfw	ADRESH		; obere  2 Bit auslesen
	movwf	f1		; obere  2-Bit nach U1H
	bsf	STATUS,RP0	; Bank1
	movfw	ADRESL		; untere 8 Bit auslesen
	bcf	STATUS,RP0	; Bank0
	movwf	f0		; untere 8-Bit nach U1L
	return


;*********************************************************************
;16 bit Adition, C-Flag bei Überlauf gesetzt
Add16                           ; 16-bit add: f := f + xw
         movf    xw0,W           ; xw0 nach W
         addwf   f0,F            ; f0 := f0 + xw0

         movf    xw1,W           ; xw1 nach W
         btfsc   STATUS,C        ; fall ein Überlauf auftrat:
         incfsz  xw1,W           ;   xw1+1 nach W
         addwf   f1,F            ; f1 := f1 + xw1

         return                  ; fertig


;*****************************************************
; 16 Bit Subtraktion, bei Überlauf (neg. Ergebnis) ist C gesetzt
Sub16                           ; 16 bit f:=f-xw 
         clrf    Fehler          ; extraflags löschen 

         movf    xw0, w          ; f0:=f0-xw0
         subwf   f0, f

         btfsc   STATUS,C
         goto    Sub16a
         movlw   0x01            ; borgen von f1
         subwf   f1, f

         btfss   STATUS,C
         bsf     Fehler, C       ; Unterlauf

Sub16a
         movf    xw1,w           ; f1:=f1-xw1
         subwf   f1    ,f

         btfss   STATUS,C
         bsf     Fehler, C       ; Unterlauf

         bcf     STATUS, C       ; C-Flag invertieren
         btfsc   Fehler, C
         bsf     STATUS, C
         return


;*****************************************************
; Division durch 2 wird w-mal ausgeführt
; die zu dividierende Zahl steht in xw
Div2 
         movwf   counter         ; Anzahl der Divisionen speichern
Div2a                           ; 16 bit xw:=xw/2
         bcf     STATUS, C       ; carry löschen
         rrf     xw1, f
         rrf     xw0, f
  
         decfsz  counter, f         ; fertig?
         goto    Div2a           ; nein: noch mal
         return

;*****************************************************
; Wandlung des ADC-Wert in Millivolt (binär)
; Der ADC-Wert steht in f1,f0
; Ergebnis steht in f1,f0
mV
	; zunächst die Multiplikation mal 5
	movfw	f0
	movwf	xw0
	movfw	f1
	movwf	xw1
	call	Add16		; f := 2xADC
	call	Add16		; f := 3xADC
	call	Add16		; f := 4xADC
	call	Add16		; f := 5xADC

	; ADC * 5 nach xw kopieren
	movfw	f0
	movwf	xw0
	movfw	f1
	movwf	xw1		; xw := 5xADC

	; xw durch 64 dividieren (6 mal durch 2)
	; dann ist xw = 5xADC/64
	movlw	6
	call	Div2
	call	Sub16		; f := 5xADC - 5xADC/64

	; xw auf 5xADC/128 verringern
	movlw	1
	call	Div2
	call	Sub16		; f := 5xADC - 5xADC/64 - 5xADC/128 
	return			; fertig


;*****************************************************
; Wandlung einer Binärzahl (< 10000) in eine Dezimalzahl
; Die Binärzahl steht in f1,f0
; die Dezimalstellen werden in ST (tausender), SH (hunderter),
;    SZ (zehner) und SE (einer) gespeichert im BCD-Code
B2D
	; Test auf tausender 1000d = 0x03E8
	movlw	0x03
	movwf	xw1
	movlw	0xE8
	movwf	xw0
	call	B2Da
	movwf	ST
	; Test auf hunderter 100d = 0x0064
	clrf	xw1
	movlw	0x64
	movwf	xw0
	call	B2Da
	movwf	SH
	; Test auf zehner 10d = 0x000A
	clrf	xw1
	movlw	0x0A
	movwf	xw0
	call	B2Da
	movwf	SZ
	movfw	f0
	movwf	SE
	return

B2Da
	clrf	counter
B2Sb	incf	counter, f	; wie oft abgezogen?
	call	Sub16		; f:=f-xw	
	btfss	STATUS, C	; zu oft abgezogen?
	goto	B2Sb		; nein: noch einmal
	call	Add16		; f:=f+xw
	decf	counter, w	; weil immer 1 zuviel gezählt wird
	return


;*****************************************************
;Anzeige der dezimalzahl am LCD mit 'mV'
; input: ST, SH, SZ, SE dezimalstellen im BCD-Code
Ausgabe1
	movlw	B'10000000'	; 1. Zeile
	call	OutLcdControl
	movlw	'0'		; 30h = '0011 0000'
	iorwf	ST, w		; BCD -> ASCII
	call	OutLcdDaten	; zum LCD
	movlw	'0'
	iorwf	SH, w
	call	OutLcdDaten
	movlw	'0'
	iorwf	SZ, w
	call	OutLcdDaten
	movlw	'0'
	iorwf	SE, w
	call	OutLcdDaten
	movlw	'm'		; 'mA' anhängen
	call	OutLcdDaten
	movlw	'V'
	call	OutLcdDaten
	return

Ausgabe2
	movlw	B'11000000'	; 2. Zeile
	call	OutLcdControl
	movlw	'0'		; 30h = '0011 0000'
	iorwf	ST, w		; BCD -> ASCII
	call	OutLcdDaten	; zum LCD
	movlw	'0'
	iorwf	SH, w
	call	OutLcdDaten
	movlw	'0'
	iorwf	SZ, w
	call	OutLcdDaten
	movlw	'0'
	iorwf	SE, w
	call	OutLcdDaten
	movlw	'm'		; 'mA' anhängen
	call	OutLcdDaten
	movlw	'V'
	call	OutLcdDaten
	return


;*****************************************************
;+++LCD-Routinen**************************************
;*****************************************************
;LCD initialisieren, Begrüßung ausgeben

InitLcd
	movlw	D'255'		; 250 ms Pause nach dem Einschalten
	movwf	loops	
	call	WAIT		

	movlw	B'00110000'	; 1
	movwf	LcdPort
	bsf	LcdE
	nop	
	bcf	LcdE
	
	movlw	D'50'		; 50 ms Pause
	movwf	loops
	call	WAIT
	
	movlw	B'00110000'	; 2
	call	Control8Bit
	movlw	B'00110000'	; 3
	call 	Control8Bit
	movlw	B'00100000'	; 4
	call 	Control8Bit

	movlw	B'00000001'	; löschen und cusor home
	call	OutLcdControl	
	movlw	B'00101000'	; 5 function set, 4-bit  2-zeilig,  5x7
	call	OutLcdControl	
	movlw	B'00001000'	; 6 display off
	call	OutLcdControl
	movlw	B'00000110'	; 7 entry mode, increment, disable display-shift
	call	OutLcdControl
	movlw	B'00000011'	; 8 cursor home, cursor home
	call	OutLcdControl
	movlw	B'00001100'	; 9 display on, Kursor aus , Blinken aus
	call	OutLcdControl
	return

;*****************************************************
; ein Steuerbyte 8-bittig übertragen
Control8Bit
	movwf	LcdPort
	bsf	LcdE
	nop
	bcf	LcdE
	movlw	D'10'
	movwf	loops
	call 	WAIT
	return

;*****************************************************
; darauf warten, daß das Display bereit zur Datenannahme ist
LcdBusy
        bsf     STATUS, RP0	; make Port B4..7 input
	movlw	B'11110000'
	iorwf   TRISB, f 
        bcf     STATUS, RP0
BusyLoop		
	bcf	LcdRs
	bsf	LcdRw		; Lesen
	bsf	LcdE
	nop
	movf	LcdPort, w
	movwf	LcdStatus
	bcf	LcdE
	nop
	bsf	LcdE		; Enable
	nop
	bcf	LcdE
	btfsc	LcdStatus, 7	; teste bit 7
	goto	BusyLoop
	bcf	LcdRw
        bsf     STATUS, RP0	; make Port B4..7 output
	movlw	B'00001111'
	andwf   TRISB, f    
        bcf     STATUS, RP0
	return	

;*****************************************************
; aus W ein Byte mit Steuerdaten zum Display übertragen
OutLcdControl
	movwf	LcdDaten
	call	LcdBusy
	movf	LcdDaten, w
	andlw	H'F0'
	movwf	LcdPort		; Hi-teil Daten schreiben
	bsf	LcdE
	nop
	bcf	LcdE		; Disable LcdBus
	swapf	LcdDaten, w
	andlw	H'F0'
	movwf	LcdPort		; Lo-teil Daten schreiben
	bsf	LcdE
	nop
	bcf	LcdE		; Disable LcdBus
	return

;*****************************************************
; aus W ein Datenbyte zum Display übertragen
OutLcdDaten
	movwf	LcdDaten
	call	LcdBusy
	movf	LcdDaten, w
	andlw	H'F0'
	movwf	LcdPort		; Hi-teil Daten schreiben
	bsf	LcdRs		; Daten
	bsf	LcdE		; Enable LcdBus
	nop
	bcf	LcdE		; Disable LcdBus	
	swapf	LcdDaten, w
	andlw	H'F0'
	movwf	LcdPort		; Lo-teil Daten schreiben
	bsf	LcdRs		; Daten
	bsf	LcdE
	nop
	bcf	LcdE		; Disable LcdBus	
	bcf	LcdRs		;
	return


;*****************************************************
;Zeitverzögerung um loops * 1 ms
; 10 MHz externer Takt bedeutet 2,5 MHz interner Takt
; also dauert 1 ms genau 2500 Befehle
; 250 Schleifen a 10 Befehle sind 2500 Befehle = 1 ms

WAIT
top     movlw   .250           ; timing adjustment variable (1ms)
        movwf   loops2
top2    nop                    ; sit and wait
        nop
        nop
        nop
	nop
        nop
        nop
        decfsz  loops2, F      ; inner loops complete?
        goto    top2           ; no, go again
                               ;
        decfsz  loops, F       ; outer loops complete?
        goto    top            ; no, go again
        retlw   0              ; yes, return from subWAIT

; pauza 1s
pause1s	MOVLW		12
		MOVWF		pause1s3
pas		call		wait3
		DECFSZ		pause1s3,1
		GOTO		pas
		return


wait3	MOVLW		255			;podprogram èekání 
		MOVWF		pause1s2	;trvá asi asi 0.2s
wait2	MOVLW		255
		MOVWF		pause1s1
wait1	DECFSZ		pause1s1,1
		GOTO		wait1
		DECFSZ		pause1s2,1
		GOTO		wait2
		RETURN					;návraz z podprogramu


	end












