	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
xw2	      equ     0x32
xw3       equ     0x33          ; 
f0		  equ	  0x24	        ; 
f1		  equ	  0x25
f2        equ     0x34
f3        equ     0x35	        ; 
counter	  equ	  0x26	        ; 
Fehler	  equ	  0x27	        ; 
ST		  equ     0x28
ST1       equ     0x36	        ; 
SZ	   	  equ	  0x29
SZ1       equ     0x37	        ; 
SH		  equ	  0x2A
SH1       equ     0x38	        ; 
SE		  equ	  0x2B
SE1       equ     0x39	        ; 
loops	  equ	  0x2C
loops3    equ     0x3A	        ; timer für wait
loops2	  equ	  0x2D
loops5    equ     0x3B	        ; timer für wait
LcdStatus equ	  0x2E      	;
LcdDaten  equ	  0x2F
	        ;


Ini_con	  Equ	  B'00000000'	; TMR0 -> Interupt disable
Ini_opt	  Equ	  B'00000100'	; 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)

;********************************************************
; El programa comienza con la inicialización


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 inicializar
	                            ; ADC einschalten
	      BSF	   ADCON0, 0	; ADON=1
	                            ; ADC-Eingang AN0 auswählen
	      BCF	   ADCON0, 5	; ADCHS2=0
	      BCF	   ADCON0, 4	; ADCHS1=0
	      BCF	   ADCON0, 3	; ADCHS0=0
	                            ; ADC speed für 5 ... 20 MHz einstellen
	      BSF	   ADCON0, 7	; ADCS1=1
	      BCF	   ADCON0, 6	; ADCS0=0
	                            ; Daten rechtsbündig
	      BSF	   STATUS,RP0	; Bank1
	      clrf	   ADCON1
	      BSF	   ADCON1, 7	; ADFM=1
	      BCF	   STATUS,RP0	; Bank0


                                ;Display initialisieren
	      call	   InitLcd

Mainloop
	      call	   ADC		    ; Spannung messen nach f1,f0
	      call	   mV		    ; Wandlung in Millivolt nach f1,f0
	      call	   B2D		    ; Wandlung in dezimal nach ST,SH,SH,SE
	      call	   Ausgabe	    ; anzeigen am LCD
	      goto	   Mainloop
Mainloop1
	      call	   ADC1		    ; Spannung messen nach f3,f2
	      call	   mV1		    ; Wandlung in Millivolt nach f3,f2
	      call	   B2D_1		    ; Wandlung in dezimal nach ST1,SH1,SH1,SE1
	      call	   Ausgabe1	    ; anzeigen am LCD
	      goto	   Mainloop1
	      


;*****************************************************
                                ; Spannung mit ADC messen
                                ; Ergebnis nach F1,f0
ADC
	      BSF	   ADCON0, 2	; ADC starten
ADC1
	      BSF	   ADCON1, 3	; ADC1 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
ADCloop1
	      BTFSC	   ADCON1, 3	; ist der ADC fertig?
	      GOTO	   ADCloop1		; nein, weiter warten
	      movfw	   ADRESH		; obere  2 Bit auslesen
	      movwf	   f3	    	; obere  2-Bit nach U1H
	      bsf      STATUS,RP0	; Bank1
	      movfw	   ADRESL		; untere 8 Bit auslesen
	      bcf	   STATUS,RP0	; Bank0
	      movwf	   f2		    ; 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 add: f := f + xw
Add16_1                           
          movf     xw0,W        ; xw0 nach W
          addwf    f2,F         ; f4 := f4 + xw0

          movf     xw1,W        ; xw1 nach W
          btfsc    STATUS,C     ; fall ein Überlauf auftrat:
          incfsz   xw1,W        ; xw1+1 nach W
          addwf    f3,F         ; f3 := f3 + 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
          
Sub16_1                         ; 16 bit f:=f-xw 
          clrf     Fehler       ; extraflags löschen 

          movf     xw0, w       ; f0:=f0-xw0
          subwf    f2, f

          btfsc    STATUS,C
          goto     Sub16a_1
          movlw    0x02         ; borgen von f1
          subwf    f3, 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
          
Sub16a_1
          movf     xw1,w        ; f1:=f1-xw1
          subwf    f3 ,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      ; Número de divisiones Guardar
Div2a                           ; 16 bit xw:=xw/2
          bcf      STATUS, C    ; llevan claro
          rrf      xw1, f
          rrf      xw0, f
  
          decfsz   counter, f   ; fertig?
          goto     Div2a        ; nein: noch mal
          return

Div2_1 
          movwf    counter      ; Número de divisiones Guardar
Div2a_1                           ; 16 bit xw:=xw/2
          bcf      STATUS, C    ; llevan claro
          rrf      xw3, f
          rrf      xw2, f
  
          decfsz   counter, f   ; fertig?
          goto     Div2a_1        ; nein: noch mal
          return
;*****************************************************
                                ; Conversión del valor ADC en milivoltios (binario)
                                ; El valor del producto está disponible en f1, f0
                                ; El resultado es en f1, f0
mV
	                            ; primero la multiplicación por 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
	      call	   Add16
	      call	   Add16
	      call	   Add16

	                            ; ADC * Copia 5 después de xw
          movfw	   f0
	      movwf	   xw0
          movfw	   f1
	      movwf	   xw1		    ; xw := 5xADC

	                            ; xw dividir por 64 (seis por x 2)
                                ; a continuación, xw = 5xADC/64
          movlw	   6
	      call	   Div2
	      call	   Sub16		; f := 5xADC - 5xADC/64
                                ; xw 5xADC/128 para reducir
          movlw	   1
	      call	   Div2
	      call	   Sub16		; f := 5xADC - 5xADC/64 - 5xADC/128 
	      return			    ; fertig
	      
mV1
	                            ; primero la multiplicación por 5

	      movfw	   f2
	      movwf	   xw3
	      movfw	   f3
	      movwf	   xw1
	      call	   Add16		; f := 2xADC
	      call	   Add16		; f := 3xADC
	      call	   Add16		; f := 4xADC
	      call	   Add16		; f := 5xADC
	      call	   Add16
	      call	   Add16
	      call	   Add16

	                            ; ADC * Copia 5 después de xw
          movfw	   f2
	      movwf	   xw0
          movfw	   f3
	      movwf	   xw1		    ; xw := 5xADC

	                            ; xw dividir por 64 (seis por x 2)
                                ; a continuación, xw = 5xADC/64
          movlw	   6
	      call	   Div2_1
	      call	   Sub16_1		; f := 5xADC - 5xADC/64
                                ; xw 5xADC/128 para reducir
          movlw	   1
	      call	   Div2_1
	      call	   Sub16_1		; f := 5xADC - 5xADC/64 - 5xADC/128 
	      return			    ; fertig


;*****************************************************
                                ; La conversión de un número binario (<10.000) en un decimal
                                ; El número binario se encuentra en f1, f0
                                ; los decimales son en ST (miles), SH (cientos),
                                ; SZ (diez) y SE (a) se almacena en código BCD
B2D
	                            ; Prueba de miles de 1000d = 0x03E8
          movlw	   0x03
	      movwf	   xw1
	      movlw	   0xE8
	      movwf	   xw0
	      call	   B2Da
	      movwf	   ST
	                            ; Prueba de cientos 100d = 0x0064
          clrf	   xw1
	      movlw	   0x64
	      movwf	   xw0
	      call	   B2Da
 	      movwf	   SH
	                            ; Prueba de diez 10d = 0x000A
	      clrf	   xw1
	      movlw	   0x0A
	      movwf	   xw0
	      call	   B2Da
	      movwf	   SZ
	      movfw	   f0
	      movwf	   SE
	      return

B2Da
	      clrf	   counter
B2Sb 	  incf	   counter, f	; ¿cuántas veces quita?
          call	   Sub16		; f:=f-xw	
	      btfss	   STATUS, C	; Deducire con demasiada frecuencia?
	      goto	   B2Sb		    ; no, otra vez
	      call	   Add16		; f:=f+xw
	      decf	   counter, w	; siempre 1 porque demasiado se cuenta
	      return
	      
B2D_1
	                            ; Prueba de miles de 1000d = 0x03E8
          movlw	   0x03
	      movwf	   xw3
	      movlw	   0xE8
	      movwf	   xw2
	      call	   B2Da_1
	      movwf	   ST1
	                            ; Prueba de cientos 100d = 0x0064
          clrf	   xw3
	      movlw	   0x64
	      movwf	   xw2
	      call	   B2Da
 	      movwf	   SH1
	                            ; Prueba de diez 10d = 0x000A
	      clrf	   xw1
	      movlw	   0x0A
	      movwf	   xw2
	      call	   B2Da
	      movwf	   SZ
	      movfw	   f2
	      movwf	   SE1
	      return

B2Da_1
	      clrf	   counter
B2Sb_1 	  incf	   counter, f	; ¿cuántas veces quita?
          call	   Sub16_1		; f:=f-xw	
	      btfss	   STATUS, C	; Deducire con demasiada frecuencia?
	      goto	   B2Sb_1		    ; no, otra vez
	      call	   Add16_1		; f:=f+xw
	      decf	   counter, w	; siempre 1 porque demasiado se cuenta
	      return


;*****************************************************
;El decimal en la pantalla LCD con 'mv'
;de entrada ST, SH, SZ, SE decimales in codigo BCD
Ausgabe
	      movlw	  0xC0	; 2. Linea
	      call	  OutLcdControl
	      movlw   'V'
	      call    OutLcdDaten
	      movlw   'o'
	      call    OutLcdDaten
	      movlw   'l'
	      call    OutLcdDaten
	      movlw   't'
	      call    OutLcdDaten
	      movlw   'i'
	      call    OutLcdDaten
	      movlw   ' '
	      call    OutLcdDaten
	      
	      movlw	  '0'		    ; 30h = '0011 0000'
	      iorwf   ST, w		    ; BCD -> ASCII
	      call	  OutLcdDaten	; zum LCD
	      movlw   0x2C
	      call    OutLcdDaten
	      movlw	  '0'
	      iorwf	  SH, w
	      call	  OutLcdDaten
	      movlw	  '0'
	      iorwf   SZ, w
	      call	  OutLcdDaten
	      movlw	  '0'
	      iorwf	  SE, w
	      call	  OutLcdDaten
	      movlw	  'V'
	      call	  OutLcdDaten
	      return
	      
Ausgabe1
	      movlw	  0x80	; 1. Linea
	      call	  OutLcdControl
	      movlw   'V'
	      call    OutLcdDaten
	      movlw   'o'
	      call    OutLcdDaten
	      movlw   'l'
	      call    OutLcdDaten
	      movlw   't'
	      call    OutLcdDaten
	      movlw   'i'
	      call    OutLcdDaten
	      movlw   ' '
	      call    OutLcdDaten
	      
	      movlw	  '0'		    ; 30h = '0011 0000'
	      iorwf   ST1, w		    ; BCD -> ASCII
	      call	  OutLcdDaten	; zum LCD
	      movlw   0x2C
	      call    OutLcdDaten
	      movlw	  '0'
	      iorwf	  SH1, w
	      call	  OutLcdDaten
	      movlw	  '0'
	      iorwf   SZ1, w
	      call	  OutLcdDaten
	      movlw	  '0'
	      iorwf	  SE1, w
	      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

;*****************************************************
; esperar, es que la pantalla está preparada para la recepción de datos
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	

;*****************************************************
; W transferidos de un octeto de datos de control a la pantalla
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

;*****************************************************
; W de un byte de datos que se transmiten a la pantalla
 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


;*****************************************************
; Tiempo de retardo del bucle 1 ms*
; 10 MHz externer Takt bedeutet 2,5 MHz interner Takt
; Así que toma una ms de 2500 comandos
; 250 loops de 10 comandos son comandos 2500 = 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


	      end












