Voltage Measurement with PIC16F883

Thread Starter

Nomel

Joined May 5, 2010
2
Hello everyone,

I am currently working on a little project here, and it's becoming rather frustrating at the moment. The project is an adjustable power supply with an LCD showing the Voltage.
After preparing the parts for the power supply and the supply for the PIC+Display, i started to work on the measurements. I'm using a PIC16F883 together with a 10 Character LC-Display. I worked my way through the datasheet and started to experiment with the PIC's ADC. I started out small - measuring 0..5 V and displaying it with 10 LED's - and worked my way up from there. I got it to function with the LCD after that.
Currently I set up a Voltage divider, so that I can measure up to 40 V.
This is where it get's problematic. The LCD shows everything just fine, except the ten's digit. I'm measuring 15 Volts from a Laptop power supply and the reading is 05,xxx. I also measured a lead-acid Battery with 11.1xx V and the display is showing 01,1. Same thing with 24 V: 04,xxx. When i hook up a little rechargeable battery with 1.29 V, it reads 01,250 V. (With another ac-adapter (3.3V) I'm getting 03,2xx).

A short review about my code-logic:
First the ADC result is multiplied with 40 (*5*8, because of the scaling)
then the binary number in mV is converted to BCD-code by
-first subtracting 10000 from the result as often as possible (then I count the subtractions and that's my first digit)
-same thing with 1000 for the rest..
-ame thing with 100 for the rest..
-same thing with 10 for the rest..
-and the rest is the last digit
-afterwards, the value 30h is added to each digit and then sent to the LCD

Here's my code: (I'm using MPLAB btw)

Rich (BB code):
; Display for Voltage Measurements

;-------------------------------------------
;	PORTC:	0----E
;			1----R/W
;			2----R/S
;	PORTB:	RB0-RB7 ---- D0-D7
;	PORTA	1--ADC
;--------------------------------------------

list		p=16f883
			#include	<P16F883.INC>

			__CONFIG    _CONFIG1, _LVP_OFF & _FCMEN_ON & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTOSCIO
			__CONFIG    _CONFIG2, _WRT_OFF & _BOR21V

			org 	0x00

			loops	equ	0x2C
			loops2	equ	0x2D
			loops3	equ	0x2E

			xw0		equ	0x22	; 
			xw1		equ	0x23	; 
			f0		equ	0x24	; 
			f1		equ	0x25	; 
			counter	equ	0x26	; 
			Fehler	equ	0x27	;
			SZT		equ	0x2C	; 
			ST		equ	0x28	; 
			SZ		equ	0x29	; 
			SH		equ	0x2A	; 
			SE		equ	0x2B	; 
[...]


; right justify
			BANKSEL	ADCON1
			bsf		ADCON1, 7
			bcf		ADCON1, 5
			bcf		ADCON1, 4
			bcf		STATUS, 5

			BANKSEL ANSEL 
			BSF 	ANSEL, 1 		;Set RA0 to analog
; Select Channel AN1
			BANKSEL	ADCON0				
			bcf    	ADCON0, 5    	; ADCHS3=0 
 	 		bcf    	ADCON0, 4   	; ADCHS2=0 
  		 	bcf    	ADCON0, 3    	; ADCHS1=0		
			bsf		ADCON0, 2		; ADCHS0=0	
; ADC Frequency, turn on ADC
			bsf		ADCON0, 7
			bcf		ADCON0, 6
			bsf		ADCON0, 0		; ADC on
			
; start a conversion
Main
			clrf	f0
			clrf	f1
			clrf	xw0
			clrf	xw1

			BANKSEL	ADCON0
			bsf		ADCON0, 1	; start
			
			movlw	D'10'		; wait a bit
			movwf	loops	
			call	WAIT
cl
			btfsc	ADCON0, 1	; done?
			goto	cl			; no, go back
			
			BANKSEL	ADRESH
			movfw	ADRESH
			bcf		STATUS, 5
			movwf	f1

			BANKSEL ADRESL
			movfw	ADRESL
			bcf		STATUS, 5
			movwf	f0

			;call	x8			; result x 8
			call	mV			; 
			call	B2D			; 
			call 	output		

			goto	Main
[...]

;*********************************************************************
;16 bit Addition, 
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 Subtraction, 
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
	;Multiply the result with 8 (Voltage Divider)
	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		; f := 6xADC
	call	Add16		; f := 7xADC
	call	Add16		; f := 8xADC

	; multiplication with 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 to xw 
	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


;*****************************************************
;Converting the Binary to BCD: STZ, St, SH, ST, SZ, SE
B2D
	; Test auf Zehntausender 10000d = 0x2710
	movlw	27h
	movwf	xw1
	movlw	10h
	movwf	xw0
	call	B2Da
	movwf	SZT
	; 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

[...]
Obviously I didn't include all of the code, I tried to narrow it down to the important parts (sorry if some comments are in German)

I can't seem to find my mistake, I've been at this for quite some time now, and I just don't see where I'm going wrong. So any help would be truly appreciated.

Thank you in advance for taking a look at this.

If more code/schematics/information is needed, I will gladly provide it.

Thomas
 

rjenkins

Joined Nov 6, 2005
1,013
I'd work through it by substituting fixed values for the ADC output and stepping through the code in MPLab.

You can also set breakponts so you don't have to step through every instruction.

You can check the memory locations and see if the results match what you expect for various input values.
 

Thread Starter

Nomel

Joined May 5, 2010
2
I'd work through it by substituting fixed values for the ADC output and stepping through the code in MPLab.

You can also set breakponts so you don't have to step through every instruction.

You can check the memory locations and see if the results match what you expect for various input values.
Well, I finally figured out how to use the Simulator in MPLAB ( at least a little) and after stepping through the code for hours I noticed that the first digit is overwritten before it is sent to the display..
So I glanced at my variable declarations.. and voila
there was the problem:

Rich (BB code):
; Display for Voltage Measurements

;-------------------------------------------
;	PORTC:	0----E
;			1----R/W
;			2----R/S
;	PORTB:	RB0-RB7 ---- D0-D7
;	PORTA	1--ADC
;--------------------------------------------

list		p=16f883
			#include	<P16F883.INC>

			__CONFIG    _CONFIG1, _LVP_OFF & _FCMEN_ON & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTOSCIO
			__CONFIG    _CONFIG2, _WRT_OFF & _BOR21V

			org 	0x00

			loops	equ	0x2C
			loops2	equ	0x2D
			loops3	equ	0x2E

			xw0		equ	0x22	; 
			xw1		equ	0x23	; 
			f0		equ	0x24	; 
			f1		equ	0x25	; 
			counter	equ	0x26	; 
			Fehler	equ	0x27	;
			SZT		equ	0x2C	; 
			ST		equ	0x28	; 
			SZ		equ	0x29	; 
			SH		equ	0x2A	; 
			SE		equ	0x2B	; 
[...]
Soo thanks, worked like a charm.. the Simulator is really powerful. I should've started using it earlier

@Markd77:
I checked out microchip's site and found some things about the AN256. That's pretty neat resource ( but too much for me to go through for today :D )
 
Top