Code:
	;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Dateiname:	Voltmeter ( Lernprogramm )
; Autor:		Schlapfi
; Datum:  		Sept. 2013			
; Kontroller:   16F676  
; Das Programm kann mit wenigen veränderungen auch für andere PIC's verwendet werden.
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Funktionsbeschreibung des Programms:
; Spannungsmessung bis 15.0 Volt. 3 stellige LED-anzeige mit Multiplex betrieb. 
; An RA0 wird eine spannung gemessen und an RC0 - RC3 im BCD code ausgegeben 
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;         10K                10K               10K
;  - 0 V  ____               ____              ____       + 15V
;  ------|____|-----+-------|____|------------|____|------
;                   | 
;  ----------- || --+--- RA0 ->
;             C 1µF
;
; Bordspsnnung ( Auto ) wird durch 3 geteilt, 
; durch 3 Widerstände a,10 K Ohm.
; Von RA0 ( AD-Eingang ) einen Kondensator 1µF nach Masse stabilisiert
; die Anzeige. ( verhindert laufen )
; Der 5 Volt Messbereich des PIC entspricht dann 15 Volt.
; Das Ergebnis der Messung wird mit dem Faktor 3 Multipliziert
; und an RC1 bis RC4 im BCD code ausgegeben.
; Katoden sind RA5 = hundert ( 10 V ) , 
; RA1 / RA4 = zehner ( einerstelle ) und Dp.,
; RA2 = einer.( nachkommastelle )
; Die Katoden werden über Transistoren gesteuert der Dp. kann direkt gesteuert werden.
; BCD ausgänge sind: RC4 = 1 , RC1 = 2 , RC2 = 4 , RC3 = 8
; diese steuern einen CMOS-4511 7-Segment-Dekoder.
; Es können auch andere BCD zu 7 Segment decoder verwendet werden.( zb. 4513 oä.) 
; Die Pinbelegung des PIC wurde wegen der Pinanordnung des 4511 so gewählt.	
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	list      	p=16F676
	#include 	<p16f676.inc>
	__CONFIG  _CP_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT & _MCLRE_OFF & _CPD_OFF
	ERRORLEVEL      -302    	; Unterdrücken BANK SELECTION MESSAGES	
;
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Deklaration der Variablen und Konstanten
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;	
schleife	EQU	H'0020'				; Anzahl der "schleifen"
help		EQU	H'0021'				; Hilfsvariable für verschiedene Aufgaben.
einer		EQU	H'0022'				; Variable für die Einerstelle der Zahl
zehner		EQU	H'0023'				; Variable für die Zehnerstelle der Zahl
hundert		EQU	H'0024'				; Variable für die Hunderterstelle der Zahl
ADC_L       EQU H'0026'				; -----------------""---------------------
ADC_H       EQU H'0027'             ; Variablen 
ADC_L1      EQU H'0028'				; weden für rechenoperationen gebraucht
ADC_H1      EQU H'0029'				; -----------------""---------------------
count       EQU H'002C'				; -----------------""---------------------
	
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Masken für das Einschalten der LEDs
; Masken sind wegen der Eingänge des C-MOS 4511 so gwählt,
; und können für andere zwecke leicht verändert werden.
#define D0_AN   B'00000000'         ; 0						
#define D1_AN	B'00010000'         ; 1 
#define D2_AN	B'00000010'		   	; 2
#define D3_AN	B'00010010'			; 3
#define D4_AN	B'00000100'			; 4
#define D5_AN	B'00010100'			; 5
#define D6_AN	B'00000110'			; 6
#define D7_AN	B'00010110'	 		; 7
#define D8_AN	B'00001000'			; 8
#define D9_AN	B'00011000'			; 9
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
w		equ	 0		; w ist Zielregister
f		equ	 1		; f ist Zielregister
		org	0		; Start bei Adresse 0
		goto	START
		org	04		; feste Interrupt-Service-Adresse
START	
		bsf     STATUS,RP0			; Auswahl Bank 1 
		call 	0x3FF				; Laden des Kalibrierungswertes 
		movwf   OSCCAL				; Schreiben des Kalibrierungswertes für den internen Takt
		
		movlw	B'00010001'			; FOCS 8 und AN0
		movwf	ANSEL
    	movlw   b'00000001'	       	;setze RA0 als Eingang und den Rest als Ausgang
    	movwf   TRISA
    	movlw   b'00000000'		    ;alle Pins von Port C sind Ausgänge
    	movwf	TRISC
   		movlw  	B'00000011'  		; Pull-ups abschalten, interne clock, Teiler 1:16
  		movwf  	OPTION_REG 			; Schreiben des OPTION-Registers	 					
						
		bcf     STATUS,RP0			; Zurück zu Bank 0 
		
	   	movlw   B'10000001'     	; Rechtsbündig, AN 0, AD an
		movwf   ADCON0   
		
		clrf	PORTA				; Löschen aller Ausgänge 
		clrf	PORTC				; Löschen aller Ausgänge 
		 
    	clrf	einer
		clrf	zehner
		clrf	hundert
		movlw	d'20'
		movwf	count 
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Hauptprogramm
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Hier wird der AD-Wandler 25 X abgefragt und zu ADC_L und ADC_H addiert.
; Entspricht einer multiplikation mit 25
			
Haupt
		clrf	ADC_L
		clrf	ADC_L1
		clrf	ADC_H
		clrf	ADC_H1
        clrf    help
        movlw   d'25'               ; 25 in W laden
        movwf   help				; W in help laden
Prog	call	ADMess				; gehe zu unterprogramm ADMess
        decfsz  help,f				; 1 von help abziehen, prüfen ob null
        goto    Prog				; nicht null gehe zu Prog
    	bcf     STATUS,C			; null erreicht lösche C bit
		movfw	ADC_L				; ADC_L in W laden
		movwf	ADC_L1				; W in ADC_L1 kopieren
		movfw	ADC_H				; -"-
		movwf	ADC_H1
		goto	ADAD
ADMess
		bsf     STATUS,RP0			; Auswahl Bank 1 
    	clrf    ADRESL			    ; niederwertigen Teil des AD-Ergebnisses löschen
		bcf     STATUS,RP0			; Auswahl Bank 0 
    	clrf    ADRESH			    ; höherwertigen Teil des AD-Ergebnisses löschen
		bsf    	ADCON0,GO    		; A/D-Wandler starten
       	btfsc  	ADCON0,GO    		; Ist der A/D-Wandler fertig?
       	goto   	$-1         		; Nein, weiter warten ( eine Zeile zurück )
			
		movf	ADRESH,w			; Dezimalwert aus ADH-Register in W schreiben
;        movlw   d'3'      ; wird mit " NOP " überschrieben dient nur zur überprüfung
		addwf	ADC_H,f				; addiere W zu ADC_H und schreibe in F
		bsf     STATUS,RP0			; Auswahl Bank 1
		movf 	ADRESL,w			; Dezimalwert aus ADL-Register in W schreiben
		bcf     STATUS,RP0			; Auswahl Bank 0
;        movlw   d'70'     ; wird mit " NOP " überschrieben dient nur zur überprüfung
						  ; ADC_H + ADC_L ergeben 12,3 Volt
    	bcf     STATUS,C			; C löschen
		addwf	ADC_L,f				; addiere W zu ADC_L und schreibe in F
    	btfsc   STATUS,C			; übertrag erfolgt ?
	    incf    ADC_H,f				; addiere eins
		return
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; ADC_L und ADC_H Werden mit 3 multipliziert
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ADAD    clrf    schleife			; lösche schleife
        movlw   d'2'				; 2 in W laden, einmal steht es schon im Speicher
        movwf   schleife			; wert aus W in schleife
ADD_3	bcf     STATUS,C			; C löschen
		movf	ADC_L1,w			; ADC_L1 in W laden
		addwf	ADC_L,f				; addiere W zu ADC_L
    	btfsc   STATUS,C			; überlauf erfolgt ? kein überlauf überspringe
	    incf    ADC_H,F				; bei überlauf addiere 1 zu ADC_H
		movf	ADC_H1,w			; lade ADC_H1 in W
		addwf	ADC_H,f				; addiere W zu ADC_H und schreibe nach F
        decfsz  schleife,f			; eins von schleife abziehen bis null, 
									; wenn null überspringe nächsten befehl.
        goto    ADD_3				; gehe zu
; normalerweise wird ADC_H und ADC_L jetzt durch 256 dividiert = 9 X durch 2 dividieren
; ADC_H ein mahl rechts ADC_L wird nicht mehr gebraucht.Das Messergebnis steht in ADC_H
        rrf     ADC_H,w         	; dividiere durch 2 und schreibe nach W
                                	; in ADC_H steht jetzt das endergebnis
        movwf   help				; gemessene Spannung ( ADC_H ) in help schreiben
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; auswertung des endergebnis von Hexa in Dezimahl
; help wird zerlegt in einer , zehner und hunderter
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        clrf    schleife
    	bcf     STATUS,C
	    movlw   d'100'	       		; lade 100 in W Register 
sub_H
        subwf	help,F         		; 100 von help abziehen und in F schreiben
    	btfsc   STATUS,C       		; übertrag erfolgt ?
    	goto    ergeb_Pos_H	   		; kein übertrag gehe zu ergeb_Pos_H	
        addwf   help,f         		; addiere W ( 100 ) zu help
        movfw   schleife	   		; schleife in W Register laden
        movwf   hundert        		; anzahl der durchläufe ist jetzt hundert
        clrf    schleife       		; lösche schleife
    	bcf     STATUS,C	   		; Carryflag löschen
	    movlw   d'10'	       		; lade 10 in W Register 
        goto    sub_Z
ergeb_Pos_H	
    	incf    schleife,F     		; schleife + 1
	    goto    sub_H          		; und noch einmal
sub_Z
        subwf	help,F         		; 10 von help abziehen
    	btfsc   STATUS,C       		; übertrag erfolgt ?
    	goto    ergeb_Pos_Z	   		; kein übertrag gehe zu ergeb_Pos_Z 
        addwf   help,f         		; addiere W ( 10 ) zu help
        movfw   schleife	   		; schleife in W Register laden
        movwf   zehner         		; anzahl der durchläufe ist jetzt zehner
        movfw   help           		; der rest
        movwf   einer          		; ist einer
    	bcf     STATUS,C
		goto	Anz
ergeb_Pos_Z	
    	incf    schleife,F			; schleife + 1
	    goto    sub_Z
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Ansteuerung einer 3 X 7 Segment Anzeige ( Multiplex )
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anz
		clrf	PORTA				; Löschen aller Ausgänge 
		clrf	PORTC
; führende null unterdrücken. ZEROPIT abfragen ob gesetzt.
		movlw	d'0'				; 0 nach " W " laden
		addwf	hundert,w			; inhalt von hundert dazu addieren, schreibe in " W "
		btfsc	STATUS,Z			; ist nicht null ? : überspringe nächste Zeile
		goto	An2					; wenn null, 6 Zeilen nach unten
		movlw	B'00100000'			; hunderter Katode
	    movwf	PORTA				; und auf PORTA ausgeben
		movf	hundert,w			; Laden der Variable "hundert" in das W-Register
		call	LED				    ; Sprung zum Laden der LED-Variablen
		movwf	PORTC				; und auf PORTC ausgeben
		call	PAUSE				; Pause 4 mSek.
An2
	  	clrf	PORTA				; Löschen aller Ausgänge 
		clrf	PORTC
    	movlw	B'00010010'			; zehner Katode
		movwf	PORTA
		movf	zehner,w			; Laden der Variablen "zehner" in das W-Register
		call	LED					; Sprung zum Laden der LED-Variablen
		movwf	PORTC
		call	PAUSE
		clrf	PORTA				; Löschen aller Ausgänge 
		clrf	PORTC
    	movlw	B'00000100'
		movwf	PORTA
		movf	einer,w				; Laden der Variablen "einer" in das W-Register
		call	LED					; Sprung zum Laden der LED-Variablen
		movwf	PORTC
		call	PAUSE
	    decfsz  count				; count minus 1 bis 0
        goto    Anz					; count nicht 0
		movlw	d'20'
		movwf	count 
		goto	Haupt				; count = 0, neue AD-Messung
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Ab hier folgen die Unterprogramme
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;Warteschleife Pause 
PAUSE								; Label, wo die Standardwarteschleife beginnt
		movlw	d'199'				; Dezimalwert für die Zeitkonstante damit "PAUSE" = 1ms ist
		movwf	schleife			; Schieben des Wertes 199 in die Variable "schleife"
									; Ende der Start-Eeinstellungen für "PAUSE1ms"
PAUSEE								; Einsprungstelle für Pausenschleife
		nop
		nop										 	
		decfsz	schleife,1			; Dekrementiere die Variable "schleife" um 1 und schreibe das Ergebnis zurück in "schleife"
		goto	PAUSEE				; Ist die Zeit verstrichen? Nein, dann springe zurück zu "PAUSEE"
									; Neuer Durchlauf bis "schleife"= 0
		return
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
LED
		addwf	PCL,1
		retlw	D0_AN	
		retlw	D1_AN	
		retlw	D2_AN	
		retlw	D3_AN
		retlw	D4_AN
		retlw	D5_AN	
		retlw	D6_AN	
		retlw	D7_AN
		retlw	D8_AN
		retlw	D9_AN
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
		END
 
Lesezeichen