PIC16F877A controlling of a digital potentiometer - PCP42100.. problems.

Status
Not open for further replies.

si2030

Member
Hi there,

I wanted to control a digital potentiometer - Microchips MCP42100. It requires the sending of two bytes serially (via an SPI Interface) from the PIC. The first byte is a command byte and controls which potentiometer (there are two in the package) to write to and the second byte is a number between 0 and 256 indicating the wiper position from 0 to 100k ohms.

One of the application notes details exactly how to interface to these ICs so I pretty much copied the code (all I changed was the ports being used). It looked pretty easy...

Basically it doesnt work and I have no idea why... below is the schematic for the way its wired up to the PIC... simple connection to the PIC PORTE 0 -> /CS, 1 -> SCK and 2 -> SO.

Again the code has been copied from AN746 "Interfacing Microchip's MCP41XXX/42XXX Digital Potentiometers to a PICmicro Microcontroller". The Application Note (attached also) is really just one page and then source code... I used Appendix D for my code and it really only consists of two key routines and some delay routines.... my code is below..

Hoping someone might find an obvious mistake... right now the potentiometer reads 0.1k and if I check between the two terminals I get 187k... not 100k as I would have expected... I should get 40k ohms with the wiper position set to D'100'.

Thanks

Simon



Code:
;	ADD INCLUDE STATEMENT.
	list	p=16f877A	; list directive to define processor
	include	<p16F877A.inc>

;	SUPPRESS ERROR WARNINGS.  
	errorlevel -302,-207	; suppress message 302 from list file
	errorlevel -305,-207	; suppress message 302 from list file
	
;	FUSES.
	__config _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _XT_OSC & _WRT_OFF & _LVP_OFF & _CPD_OFF

;=============================================================================
; DECLARATIONS
;=============================================================================
RESET_V			equ	0X0000		; ADDRESS OF RESET VECTOR.
ISR_V			equ	0X0004		; ADDRESS OF INTERRUPT VECTOR.
OSC_FREQ 		equ D'4000000'	; OSCILLATOR FREQUENCY IS 4 MHZ.

;POTENTIOMETER VARIABLES
CS 				EQU 0			;PORTE: <1> CHIP SELECT
SCLK 			EQU 1			;PORTE: <2> SERIAL CLOCK
SI 				EQU 2			;PORTE: <3> SERIAL DATA

; REGISTERS.
		cblock  H'20'       ; NOTE: START DATA BLOCK.


;DELAY VARIABLES.
DELAY_VAL					; USED IN DELAY ROUTINES.
X_DLY_VAL					; "

;POTENTIOMETER VARIABLES.
OUT
COMMAND1
COMMAND2
R_VALUE1
R_VALUE2

COUNT
		endc				; NOTE: END OF DATA BLOCK.
		
		
		org		RESET_V		; RESET vector location
		reset	goto	MAIN ; JUMPING TO AVOID THE INTERRUPT VECTOR.		
		
MAIN
						
		call	WAIT_15MS	;GIVE THE MICRO A CHANCE TO SETTLE DOWN.

		clrf	STATUS		; DO INITIALIZATION, SELECT BANK 0
		clrf	INTCON		; CLEAR INT-FLAGS, DISABLE INTERRUPTS
		clrf	PCLATH		; KEEP IN LOWER 2KBYTE

		clrf	PORTA		; ALL PORT OUTPUT SHOULD OUTPUT LOW.
		clrf	PORTB		; ALL PORT OUTPUT SHOULD OUTPUT LOW.
		clrf	PORTC		; ALL PORT OUTPUT SHOULD OUTPUT LOW.
		clrf	PORTD		; ALL PORT OUTPUT SHOULD OUTPUT LOW.
		clrf	PORTE		; ALL PORT OUTPUT SHOULD OUTPUT LOW.
	
		bcf		STATUS, RP1	; FOR BANK 1 RP1=0 RP0=1.
		bsf		STATUS, RP0	; SELECT BANK 1.

		movlw	B'00000111'
		movwf	ADCON1		;SETTING FIRST THREE PINS TO DIGITAL I/O.

		clrf    PORTA		;LATCH ALL LOW.
		movlw   B'00000000'	;SET RA4-RA7 TO OUTPUTS. 
		movwf	TRISA
					
		clrf    PORTB		;LATCH ALL LOW.
		movlw   B'11110011'	;SET RB0/INT - RB1 AND RB4-RB7 TO INPUTS. 
		movwf	TRISB	
			
		movlw	B'00000000'	;SET RD4-7 TO OUTPUTS.
		movwf	TRISD
		
		movlw	B'00000000'	;SET RE0-2 TO OUTPUTS.
		movwf	TRISE

		bsf		OPTION_REG, NOT_RBPU ; DISABLE WEAK PULL-UPS ON PORT B.

							;NOTE - We want to know which edge the interrupt
							; is triggering off. This set the RISING edge
							; as the triggering edge.

		bsf		OPTION_REG, INTEDG ; SET RB0/INT TO RESPOND TO RISING
							;		 LEADING EDGE.
		
		bcf		STATUS, RP1	; FOR BANK 0 RP1=0 RP0=0.
		bcf		STATUS, RP0	; SELECT BANK 0.

							;NOTE - There are two of pages for the FSR.
							; we want are working with the first page.

		bcf		STATUS,IRP	;MAKING SURE THAT THE FIRST PAGE OF THE FSR
							; IS SELECTED.	
							
							
;*******************************************************************************		
;SET OUR VALUE TO 100 OUT OF A POSSIBLE 256 POSITIONS.... (PAGE16 DATASHEET)

;THEREFORE SET WIPER RESISTANCE BETWEEN PA AND PW (WIPER) TO:
;
;RWA = ((100K * (256 - 100))/256)+52
;RWA = 60.9K

;RWB = ((100K * (100))/256)+52
;RWB = 39K
;
;ADDS TO 100K.
;
;USING THE PROGRAMMING INSTRUCTIONS FROM AN746.
;
;HERE WE SET OUR "R_VALUE1" TO D'100' AND SET OUR COMMAND TO H'13'. THEN
;WE SET CS LOW AND SEND TWO BYTES TO THE CHIP VIA PORTE.
;*******************************************************************************
START
		bsf 	PORTE, CS 	;SET PORTE CS HIGH SO WE CAN SET IT LOW LATER.
	
		movlw	D'100'
		movwf	R_VALUE1	
		
		movlw	H'13'		;JUST SET BOTH POTS FIRST JUST TO GET IT WORKING.
		movwf	COMMAND1	
		
				
		bcf 	PORTE, CS 	;SELECT THE POT BY BRINING CS TO GROUND.	
			
		movlw 	COMMAND1 	;LOAD THE COMMAND BYTE IN THE ACCUMULATOR B '00010011'
		call 	TRANSMIT 	;TRANSMIT THE COMMAND BYTE
		
		movlw 	R_VALUE1 	;LOAD THE RESISTANCE VALUE IN THE ACCUMULATOR
		call 	TRANSMIT 	;TRANSMIT THE RESISTANCE VALUE
		
		bsf 	PORTE, CS 	;SET CS HIGH.
	
		
;*******************************************************************************
;FINISH LOOP.
;*******************************************************************************					
FIN							;FINISH!

		goto	FIN




;*******************************************************************************
;THIS IS OUR TRANSMIT ROUTINE AS PER AN746.
;*******************************************************************************		
TRANSMIT
		movwf 	OUT 		;MOVE W TO ’OUT’ VARIABLE
		
 		movlw 	0X08 		;LOAD A COUNTER TO ’COUNT’ THE BIT
		movwf 	COUNT		;TRANSMISSION
		
L_SHIFT 
		btfsc 	OUT, 7		;IS THE 7TH BIT A "1"?
		goto 	HI			;YES. ITS A "1" SO GOTO HI AND CLOCK IN A "1".
		
		bcf 	PORTE, SI 	;NO. ITS A "0" SO CLOCK IN A ZERO.
		
		goto 	PASS
		
HI 
		bsf 	PORTE, SI 	;ITS A "1" SO CLOCK IN A "1".
		
PASS 
		bsf 	PORTE, SCLK ;SET SERIAL CLOCK: HI
		
		rlf 	OUT, F 		;ROTATE OUT LEFT
		
		bcf 	PORTE, SCLK ;SET SERIAL CLOCK: LOW
		
		decfsz 	COUNT, F 	;DECREMENT COUNTER UNTIL ITS ZERO
		goto 	L_SHIFT
		
		clrf 	PORTE 		;WHEN COUNTER IS ZERO IT’S END OF TRANSMISSION
		
		return 				;RETURN FROM SUBROUTINE




;*******************************************************************************
;DELAY ROUTINES.
;*******************************************************************************		
WAIT_A_SEC	
		call WAIT_500MS
		call WAIT_500MS
		
		return
		
WAIT_750MS	
		call WAIT_256MS
		call WAIT_256MS
		call WAIT_256MS
		
		return
		
WAIT_500MS	
		call WAIT_128MS
		call WAIT_128MS
		call WAIT_128MS
		call WAIT_128MS

		return
		
WAIT_256MS	
		call WAIT_128MS
		call WAIT_128MS
		
		return
		
WAIT_128MS	
		movlw	D'240'		; 128 msec
		movwf	X_DLY_VAL
		
LOOP_128MS	
		call	DELAY		;WAIT 500uSec
		decfsz	X_DLY_VAL, F
		goto	LOOP_128MS
		
		return
		
WAIT_64MS	
		movlw	D'120'		; 64 msec
		movwf	X_DLY_VAL	;

		
LOOP_64MS	
		call	DELAY		;WAIT 500uSec
		decfsz	X_DLY_VAL, F
		goto	LOOP_64MS	
		
		return
		
WAIT_32MS	
		movlw	D'60'		; 30 msec
		movwF	X_DLY_VAL
	
LOOP_32MS	
		call	DELAY		; WAIT 500uSec
		decfsz	X_DLY_VAL, F
		goto	LOOP_32MS
		
		return


WAIT_15MS	
		movlw	D'30'		; 15 msec
		movwf	X_DLY_VAL
	
LOOP_15MS	
		call	DELAY		;wait 500uSec
		decfsz	X_DLY_VAL, F
		goto	LOOP_15MS

		return
		
WAIT_4MS	
		movlw	D'08'		; 4 msec
		movwf	X_DLY_VAL
		
LOOP_4MS	
		call	DELAY		;wait 500uSec
		decfsz	X_DLY_VAL, F
		goto	LOOP_4MS
		
		return

WAIT_1MS	
		movlw	D'02'		; 4 msec
		movwf	X_DLY_VAL
	
LOOP_1MS	
		call	DELAY		;wait 500uSec
		decfsz	X_DLY_VAL, F
		goto	LOOP_1MS
		
		return

;DELAY ROUTINE = 500uSec OR 1/2 A MILISECOND.
DELAY		
		movlw	H'FF'		; +1		1 CYCLE
		movwf	DELAY_VAL	; +2		1 CYCLE
		
DELAY_LOOP	
		decfsz	DELAY_VAL, F; step 1	1 CYCLE
		goto	DELAY_LOOP	; step 2	2 CYCLE
		return				; +3		2 CYCLE
				
		END
 

Attachments

  • MPC42100.jpg
    392 KB · Views: 560
  • 00746a.pdf
    193.2 KB · Views: 400
hi Simon,
Look at this Oshonsoft Scope plot.
It looks as though you have the clock bits out of sync with the data.;

LOAD THE COMMAND BYTE IN THE ACCUMULATOR B '00010011'
 

Attachments

  • AAesp06.png
    8.3 KB · Views: 297
Last edited:
hi Simon,
Here is your problem...

Code:
START
        bsf     PORTE, CS     ;SET PORTE CS HIGH SO WE CAN SET IT LOW LATER.
    
        movlw    D'100'
        movwf    R_VALUE1    
        
        movlw    0x13        ;JUST SET BOTH POTS FIRST JUST TO GET IT WORKING.
        movwf    COMMAND1    
        
                
        bcf     PORTE, CS     ;SELECT THE POT BY BRINING CS TO GROUND.    
            
       [COLOR=Red] ;movlw     COMMAND1   [/COLOR]  ;LOAD THE COMMAND BYTE IN THE ACCUMULATOR B '00010011'
;movlw moves the[B] address[/B] of Command into Wreg, not the contents of the address

        movf     COMMAND1,W     ;LOAD THE COMMAND BYTE IN THE ACCUMULATOR B '00010011'
        call     TRANSMIT     ;TRANSMIT THE COMMAND BYTE
        
       [COLOR=Red] ;movlw     R_VALUE1[/COLOR]     ;LOAD THE RESISTANCE VALUE IN THE ACCUMULATOR
;movlw moves the[B] address[/B] of Command into Wreg, not the contents of the  address

        movf     R_VALUE1,W     ;LOAD THE RESISTANCE VALUE IN THE ACCUMULATOR
        call     TRANSMIT     ;TRANSMIT THE RESISTANCE VALUE
        
        bsf     PORTE, CS     ;SET CS HIGH.
    
        
;*******************************************************************************
;FINISH LOOP.
;*******************************************************************************                    
FIN                            ;FINISH!

        goto    FIN
 

Attachments

  • AAesp10.gif
    54.3 KB · Views: 345
Last edited:
Firstly, thanks Eric for taking the time to have a look at this. I made the changes to the code... I understand the error I made now... however I am getting some really erroneous results with the chip. I have run the program twice first with D'100' as the wiper position between 0 and 256 and then with D'200'...

Here are the results measured using a multimeter...

D'100' D'200'​
PA0 - PW 27.3K 17.5K
PB0 - PW 39.2K 45.0K
PA0 - PB0 66.7K 64.1K


D'100' D'200'​
PA1 - PW 27.4K 17.4K
PB1 - PW 38.4K 44.4K
PA1 - PB1 65.9K 63.8K

So from this I think the program worked... as I get the same values on both pots.. however I must be missing something as the resistances do not add up to 100k.... really just 2/3rds of 100k.

I also replaced the chip that I used first with another (I have four of these) and got pretty much identical results... so confused...

Is this a chip where I have to ground every unused pin including all the unused PA PB PW etc ...

Simon
 
hi Simon,
How are you measuring the resistance across the 'ends' of the pots.?

Also have you tried wiring as per the Appendix F, in the datasheet, using their voltage method
 
Hi Eric

I didnt find an appendix F but section 2.1 Parmetric tests have a number of test circuits... I tried the setup as per Figure 2-25 "Potentiometer Divider Non Linearity Error Test Circuit". I had 100 set in the program. so using V*100/256 =2.00volts. which is exactly what I got when I measured the voltage... so it works... I am confused though...

So Why cant I simply measure the ends and the resistance between the wiper? Why was I getting incorrect values (as measured)... can I trust this to work in a variable gain op amp circuit??
 

hi,
Is the pot connected to the circuit when you are measuring its resistance.??
If yes, then you are measuring the parallel resistance of the pot and its connected components.?
Look at this image from App F.
 

Attachments

  • AAesp01.gif
    13.8 KB · Views: 388
Hi Eric,

Thats the set up I used and it worked... Its when I measure between PA and PB and then between PA and PW or PB and PW... shouldnt it come to 100k between PA and PB and "x" and '100k-x" between the wiper and the ends?
 
Hi Eric,

Thats the set up I used and it worked... Its when I measure between PA and PB and then between PA and PW or PB and PW... shouldnt it come to 100k between PA and PB and "x" and '100k-x" between the wiper and the ends?

hi,
With the pot connected as shown in that image, what voltage do you get on the wiper, when you program the pot for: 0k, 25K, 50K,75K and 100K when using a 5V source voltage..?
 
Last edited:
Hi Eric,

Right... did that. Here are the results:

0k 0 D'1' 0.02
25k 1.25v D'64' 1.26v
50k 2.50v D'128' 2.52v
75k 3.75v D'192' 3.79v
100k 5.00v D'255' 5.05v

The regulated voltage to the chip (and the micro) is 5.07volts..

So it works perfectly...

I was just wondering why I cant just check the ends and get the correct resistance with a multimeter? In this case 100k between PA and PB...

By the way thanks for taking the time on this...
 

hi Simon,
As its basically a semiconductor switching the resistive elements within the IC, using a meter on ohms [that uses an internal battery] you could get misleading resistance readings.
Are you planning to use the 'pot' as the feedback resistor in a OPA.?
 
Hi Eric..

Yes I was going to... I was going to make a variable gain amplifier with two stages and then program the pots to make it as linear as possible.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…