Monday, 2 February 2015

MCP3002 with a 10K Pot on Raspberry Pi using SPI GPIO Pins and Python

This project I decided to work with the SPI GPIO ports on the Pi. Part of the kit I had bought for the Pi had an MCP3002 IC . This is a very common ADC (2 Channel - Analogue to Digital Converter). This is a very useful IC to connect to temperature sensors (or anything that change the voltage), which can output a voltage that is related to the temperature (a conversion needs to be done, but we will get to that later)

I sat for weeks trying to understand how this device works and for the life of me a I could not find a site that explained what their code was actually doing. Till one day I sat down with a pen and paper and a Binary to Hex and Decimal converter. I finally figured it out and thought I would right this blog to help anyone else out there that may be in the boat and almost give up with dispair.


The board layout

This is what you will need (ingredients):
  • 1 x Rasperry Pi
  • 1 x Breadboard
  • 1 x 10K Pot
  • 1 x 10uf Capacitor
  • And a few jump cables
Here is the setup I have :




  • GPIO 1 (3.3v) -> + Breadboard (3.3v)
  • GPIO 6 (Ground) -> - Breadboard (Ground)
  • 10uf Capacitor -> One pin in line with MCP3002 Pin 8, Other Pin to Ground
  • MCP3002 Pin 8 (Vdd/Vref) -> + Breadboard (3.3v)
  • MCP3002 Pin 7 (CLK) -> GPIO 11 (SPIO_SCLK)
  • MCP3002 Pin 6 (Dout) -> GPIO 9 (SPIO_MISO)
  • MCP3002 Pin 5 (Din) -> GPIO 10 (SPIO_MOSI)
  • MCP3002 Pin 4 (Vss) -> - Breadboard (Ground)
  • MCP3002 Pin 3 (CH1) -> Not connected
  • MCP3002 Pin 2 (CH0) -> Center pin of 10K Pot
  • MCP3002 Pin 1 (CS) -> GPIO 8 (SPIO_CE0_N)
  • 10K Pot Pin 1 -> - Breadboard (Ground)
  • 10K Pot Pin 3 -> + Breadboard (3.3v)

The Python Code

Now here is the cool part. The python code needed to get all the information out :
import spidev

import time

spi = spidev.SpiDev()
spi.open(0, 0)

while True:
    result = spi.xfer2([104, 0])
    digital_code = int(((result[0] & 3) << 8) + result[1])
    voltage = round(((digital_code * 3.33) / 1024), 2)
    print voltage
    time.sleep(0.5)
 And this is where things need a little explaining. Why do we use the values we do in the function's list to communicate with the MCP3002 ? What and why we use the bit manipultion ?

spidev is the module needed to allow python to talk through the SPIO pins. From a console run the following :
sudo apt-get install spidev

Initialization

spi.open(0, 0) -> Open SPIO Port 0 with CS 0. Basic initialization of the SPIO Pins. This tells the Pi to communicate down SPIO Port 0, which is attached to the CS (Channel Select) pin on the MCP3002. This tells the MCP3002 it needs to return information from CH0 (Channel 0) pin. If you had the pot on CH1 and connected to GPIO 7 (SPIO_CE1_H) then you would have code - spi.open(1, 1)


Transmitting Data

spi.xfer2([..., ...]) -> Now this is an interesting function that had me trying to understand what was being put in that list. Many sites I looked at had different values, but none really explained how they got these values, but through a little bit of maths and base conversions I found it out. With an MCP3002 you need only put in 2 list values to get a result. MCP3004/8 use 3 list values (I will get to that later). Have a look at this taken from the MCP3002 DeviceDoc :



Let me try and explain it the best I can in which made me figure out the workings of the IC. Don't worry too much about falling and rising of edge of clock. We are more looking at the bits that need to be transferred/received to/from the device. This IC works with 8 bit blocks (a byte if you wish). From this we can see that what needs to transferred to the device to get a response is two sets of 8 bits. In the first block of 8 bits (MCU Transmitted Data) we need to send some information to tell the IC how it should work for us. 


Bit Value Explanation
Start Bit 1 To initialize the IC to receive data
SGL/DIFF 1/0 1 - To select Single Ended Mode (This is the mode we will use)
0 - To select Pseudo Differential Mode
ODD/SIGN 0/1 Channel Selection. CH0 or CH1
MSBF 1/0 1 - To select MSBF - Most Signification Bit First
     Bits returned in normal order with first bit starting on the left
0 - To select LSBF - Last Signification Bit First
     Bits returned in reverse order with first bit starting from the right

As you can see this is the order of the bits and how they are needed:

  1. Ignored
  2. Set to 1 (Start Bit)
  3. Set to 1 (To select Single Ended Mode)
  4. Set to 0 (To talk to Channel 0)
  5. Set to 1 (To have the returned bits in the right order to process)
  6. Ignored
  7. Ignored
  8. Ignored
So the bits, in order, that we need are 1101. But that's only 4 bits you say. So we need to shift these over by 3 bits to give us a binary value of 1101000 (Remember the 1st bit is ignored). We will pretend there is a 0 there as the first bit, so for completeness we can look at it as 01101000. That is the 8 bits we need to send. If you convert that to a Decimal value it will give you the number 104. This is the first value in the list.

On the second set of 8 bits that get sent. Looking at the same excerpt above we can see that what ever is put in there is ignored. So we just send a Decimal value of 0 (00000000 - in binary).

So now you see how we get 104, 0 

Receiving Data

As you can see result receives the information that spi.xfer2[(104, 0]) sends.

This is also returned as a list. The reason for this can once again be seen in the excerpt above (MCU Received Data). Two blocks of 8 bits. The first block holds 2 bits of the final code and the second block holds the last 8 bits of the code. The total final resulting code is actually a 10bit number. These two blocks of 8 bits are returned as to values in a list (result) which we need to bung together to produce the digital_code.

How we do this requires a little Bit Manipulation.

Lets look at the code to do this conversion. int(((result[0] & 3) << 8) + result[1])

The first block we see that only the last 2 bits are needed, though the device could send back more. This is position 0 in the list of result. Therefore result[0]. Using the Bitwise operator & with the decimal value of 3 which has a binary of 11 to make sure that we only get the last 2 bits. Here is a few examples to show what the & operator does:

  • 1010 & 0011 = 0010
  • 1001 & 0011 = 0001
  • 0111 & 0011 = 0011
So with the final value of result[0] & 3 we now need to shift it 8 places to the left to make space for the next 8 bits that need to be added to it (Basically add 8 zero's to the end of the binary number). That is what << 8 does. Here are a few examples to explain this :
  • 11 << 8 = 1100000000
  • 01 << 8 = 0100000000
  • 10 << 8 = 1000000000

Now that we have taken the two bits out, and shifted it over by 8 we now add the final number in the list, result[1], to it. This gives us a final decimal number that is the digital_code.

Calculating the voltage

Once we have the digital_code we now need to do a simple calculation to convert that number into the voltage that is being returned by the pot. This is taken from the MCP3002 documentation :

So what we need to find is Vin. We know Vdd is 3.33v (Tested on a volt meter). And we know what the digital_code is. Then, with a little manipulation of the algorithm, we get (digital_code * 3.33) / 1024). using the round function we round it off to two decimal places and therefore get the final calculation of  round(((digital_code * 3.33) / 1024), 2).

Conclusion

At the end of all this we a voltage that is returned by the pot. This comes in useful for things like temperature sensors that produces a resistance relevant to the temperature. Producing this resistance lowers the voltage. This final voltage can be used to calculate what the temperature is actually being sensed by the sensor.

I hope this has help you as much as possible. If you have any questions feel free to post it and I will endeavour to answer it the best I can.

Look out for my follow on post on the MCP3004/8 IC coming soon. 



No comments:

Post a Comment