HSG

Aktuelle Seite: HSG/Fächer/Informatik/ MSR/PIC/Programme

Versuchsaufbau Viele PICs haben ein sogenanntes Analog-to-Digital-Converter-Modul, mit dem es möglich ist, Spannungen einzulesen. Im Falle des 16F877a hat der ADC 8 Kanäle, dh. die Ports A0-A7 können softwaregesteuert an den ADC gelegt werden. Die Auflösung beträgt dabei 10 Bit, dh. das Ergebnis einer Wandlung ist eine Zahl zwischen 0 und 1023. Jörg Bredendiek (sprut) hat ein schönes Lernbeispiel zur Benutzung des ADC veröffentlicht. Sein Programm wurde an das Deltawave-Board angepasst und ist auch zur Erarbeitung der Ansteuerung eines LCD-Displays sehr hilfreich. Das Archiv adc_sprut.zip enthält den Assembler-Quelltext und das Hex-File zum direkten Brennen. Die Spannung wird am PORTA,0 (gelb) gemessen. Im Versuchsaufbau wurde ein 10k-Potentiometer als Spannungsteiler zwischen Masse (schwarz) und +5V (rot) verwendet

Quelltext

; sprut-Programm von mk an Deltawave 16f877a angepasst
	list p=16f877a
;**************************************************************
;*  	Pinbelegung
;*	----------------------------------
;*	PORTA: 	0  Interupt disable
Ini_opt		Equ	B'00000010'	; pull-up

; für LCD-Pins
#define		LcdE   	PORTD,3		; enable Lcd
#define		LcdRw 	PORTD,2		; read Lcd
#define		LcdRs 	PORTD,1		; Daten Lcd (nicht control)
#define 	LcdPort		PORTD		; Datenbus des LCD (obere 4 Bit)

;********************************************************
;mk------------------------------------------------------
	org 	0x00

RESET:	clrf 	STATUS
	movlw 	0x00
	movwf 	PCLATH
	goto 	main

	org	0x10

; Das Programm beginnt mit der Initialisierung

main
;mk------------------------------------------------------
Init	bsf     STATUS, RP0		; Bank 1
	movlw   Ini_opt     	; pull-up on
	movwf   OPTION_REG
	movlw	B'00000000'		; PortD alle outputs
	movwf	TRISD
	bcf     STATUS, RP0		; Bank 0
	clrf	PORTD
	movlw   Ini_con     	; Interupt disable
	movwf   INTCON

; ADC initialisieren
	; 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


;*****************************************************
; 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
Ausgabe
	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	' '
      	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 D4..7 input
      	movlw	B'11110000'
      	iorwf   TRISD, 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   TRISD, 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


	end

Links

Valid XHTML 1.0!