0

Voltage Divider 100k Pot added to mcp3004 channel 1Implemented a generic Pi/MCP3004 sketch to read a Thermistor with Channel 0 ... this has been working great for well over 3 years in an unattended mountaintop radio shack (providing remote monitoring of the shack temperature) ... today we added a 100k Pot ... Input Voltage (VBat) on one end of the pot the other end is Ground with the wiper attached to Channel 1 input (pin 2) ... the wiper was pre-adjusted to 3.3 V with a 20V input voltage (approx 16k to Gnd) ... the MCP3004 Vref is 3.3V ... the expectation was any reading from Channel 1 would provide VBat as (chn1 reading)/1024*20 to provide the actual voltage ... the Battery Voltage is expected in the range of 12.4V to 16V (Victron lower/upper cutoffs) ... the issue: as soon as I attach the battery to VBat (top of pot) both the Thermistor (chn0) and (chn1) read 0 ... Physical Channel Voltage readings are 1.2V and 2.2V respectively (2.2 V on Chn 1 should correspond to Vbat of 13.6V) ... the Python function that reads the chip hasn't changed ... it only requires a Port # as a parameter (code attached below) ... removal of the + Vbat from pot connected to chn1 restores readings on chn0

The MCP3004 documentation indicates there is a distinct AGnd for the channel inputs ... looking at the implemented schematic it appears AGnd and DGnd were tied together ... the expectation is separating grounds is typically done for Noisy environments ... there are other details regarding Single-ended vs Differential ... this failure suggests the chip may be in Differential mode ... The code below indicates the channels should be in single-ended mode ... SO ... the big question is: how/why is Vbat wiping out Channel 0 and not providing a reading for Channel 1 ... has anyone experienced a failure like this?

Python Code

#----------------- readADC( Channel# ) ---------------------------------------
# Microchip ADC MCP300X family has 3 variations; 2, 4 or 8 Channel 10bit A/D
# all 3 use a Serial Data Pin to Clock in Commands & Clock out Digital Data
# representing the Analog values
#
def readAdc(iChn): # MCP3004 has 4 Channels: 0 to 3

 # ensure both pgl and pg don't attempt to read adc at the same time
 bArf = GPIO.input(ARF[_PIN]) # is there a Read in Progress
 while bArf == 1:
  bArf = GPIO.input(ARF[_PIN])
 # wend

 GPIO.output(ARF[_PIN], GPIO.HIGH) # set Read in Progress

 # Datasheet says chip select must be pulled high between conversions
 GPIO.output(CS[_PIN],  GPIO.HIGH)

 # Start the read with clock high and chip select low
 GPIO.output(CS[_PIN],  GPIO.LOW)
 GPIO.output(CLK[_PIN], GPIO.HIGH)

 # ADC commands are 5 bits long:
 #   start bit = 1
 #   single-ended comparison = 1 (vs. pseudo-differential = 0)
 #   channel num bit 2 (MSB)     (for MCP3004 this is always 0)
 #   channel num bit 1
 #   channel num bit 0 (LSB)
 # ie for single-ended channel 0 this command should be 1 1 0 0 0 or 0x18
 iAdcCmd = 0x18  # base command for Channel 0
 iAdcCmd |= iChn # add Channel to command

 for iBit in range(5):

  # output MSB bit: HI or LO
  if iAdcCmd & 0x10: # is MSB HI?
   GPIO.output(MOSI[_PIN], GPIO.HIGH)
  else:
   GPIO.output(MOSI[_PIN], GPIO.LOW)
  # endif

  # Shift next bit to MSB
  iAdcCmd <<= 1

  # Send a clock pulse HI then immediately LO
  GPIO.output(CLK[_PIN], GPIO.HIGH)
  GPIO.output(CLK[_PIN], GPIO.LOW)
 # next

 iAdcVal = 0

 for iBit in range(12):

  # Pulse clock pin
  GPIO.output(CLK[_PIN], GPIO.HIGH)
  GPIO.output(CLK[_PIN], GPIO.LOW)

  # Read 1 data bit in
  if GPIO.input(MISO[_PIN]):
   iAdcVal |= 0x1
  # endif

  # Shift in next bit
  iAdcVal <<= 1

 # next

 # Divide by two to drop the stop bit
 iAdcVal = (iAdcVal/2)
 iAdcVal &= 0x3FF

 # Set chip select high to end the read
 GPIO.output(CS[_PIN], GPIO.HIGH)
 GPIO.output(CS[_PIN], GPIO.LOW)

 GPIO.output(ARF[_PIN], GPIO.LOW) # clear Read in Progress
 return iAdcVal

# end readACD





   
1
  • Seems to be an electronics question.
    – joan
    Commented Oct 27, 2022 at 7:27

1 Answer 1

1

Solved

The original code was using the clock (CLK) incorrectly causing the COMMAND logic to drop the START bit which results in the CHIP being configured to Differential Mode ... command 0x18 is incorrectly being clocked in as 0x10 ... Any "unmatched" input on ch 1 in Differential mode basically wipes out both the Ch 0 and 1 readings (ADC output is meaningless)

Corrected code:

def readAdc(iChn): # MCP3004 has 4 Channels: 0 to 3
 # ensure both pgl and pg don't attempt to read the ADC at the same time
 # if CS is low then an ADC read is in progress 
 bArf = 0
 while bArf == 0:
  bArf = GPIO.input(CS[_PIN])
 # end while 

 # Start the read with clock high and chip select low
 GPIO.output(CLK[_PIN], GPIO.HIGH)
 GPIO.output(CS[_PIN],  GPIO.LOW)
    
 # ADC commands are 5 bits long:
 #   start bit = 1
 #   single-ended comparison = 1 (vs. pseudo-differential = 0)
 #   channel num bit 2 (MSB)     (for MCP3004 this is always 0)
 #   channel num bit 1
 #   channel num bit 0 (LSB)
 # ie for single-ended channel 0 this command should be 1 1 0 0 0 or 0x18
 iAdcCmd = 0x18  # base command for Channel 0
 iAdcCmd |= iChn # add Channel to command

 # Write Command
 for iBit in range(5):

  # Write out 1 bit of data to Pi GPIO pin connected to Din
  # output MSB bit: HI or LO 
  if iAdcCmd & 0x10: # is MSB HI?
   GPIO.output(MOSI[_PIN], GPIO.HIGH)
  else:
   GPIO.output(MOSI[_PIN], GPIO.LOW)
  # endif
      
  # Shift next bit to MSB
  iAdcCmd <<= 1

  # Falling Clock Edge to move to next bit          
  GPIO.output(CLK[_PIN], GPIO.LOW)
  # Rising Clock Edge to clock in the data waiting on Din
  GPIO.output(CLK[_PIN], GPIO.HIGH)
   
 # next

 iAdcVal = 0

 # Read Data
 for iBit in range(12):

  # Falling Clock Edge to move to next bit
  GPIO.output(CLK[_PIN], GPIO.LOW)
  # Rising Clock Edge to clock out the data from Dout 
  GPIO.output(CLK[_PIN], GPIO.HIGH)
        
  # Read in 1 data bit from Dout
  if GPIO.input(MISO[_PIN]):
   iAdcVal |= 0x1
  # endif
        
  # Shift in next bit
  iAdcVal <<= 1

 # next 
  
 # Divide by two to drop the stop bit (there is no stop bit!)
 # leave this code in place to provide a buffer between reads
 iAdcVal = (iAdcVal/2)
 iAdcVal &= 0x3FF

 # Set chip select high to end the read
 GPIO.output(CS[_PIN], GPIO.HIGH)
    
 return iAdcVal

# end readACD 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.