Tuesday, 3 February 2015

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

As mentioned in my previous post, on the MCP3002 (2 Channel - Analogue to Digital Converter), here is the setup for the MCP3008 (8 Channel - Analogue to Digital Converter). Much of it is a repeat of what I have posted in there, but to help you from bouncing to and fro trying to get it all out, I have put it altogether here.


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 MCP3008 Pin 16, Other Pin to Ground
  • MCP3008 Pin 16 (Vdd) -> + Breadboard (3.3v)
  • MCP3008 Pin 15 (Vref) -> + Breadboard (3.3v)
  • MCP3008 Pin 14 (AGND) -> - Breadboard (Ground)
  • MCP3008 Pin 13 (CLK) -> GPIO 11 (SPIO_SCLK)
  • MCP3008 Pin 12 (Dout) -> GPIO 9 (SPIO_MISO)
  • MCP3008 Pin 11 (Din) -> GPIO 10 (SPIO_MOSI)
  • MCP3008 Pin 10 (CS) -> GPIO 8 (SPIO_CE0_N)
  • MCP3008 Pin 9 (DGND) -> - Breadboard (Ground)
  • MCP3008 Pin 1 (CH0) -> Center pin of 10K Pot
  • MCP3008 Pin 2-7 (CH1-8) -> Not connected
  • 10K Pot Pin 1 -> + Breadboard (3.3v)
  • 10K Pot Pin 3 -> - Breadboard (Ground)

The Python Code

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

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

while True:
    result = spi.xfer2([1, (8 + channel) << 4, 0])
    digital_code = int(((result[1] & 3) << 8) + result[2])
    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 on the Raspberry Pi. 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). And so if you wanted to get info from any other channel from 2 to 7 the second digit in the list correspond to that channel number.


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 MCP3008 you need to put in 3 list values to get a result. Have a look at this taken from the MCP3008 Datasheet :



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 three sets of 8 bits. In the first block of 8 bits (MCU Transmitted Data) we need to send a start bit. This is just a value of 1. The next (second) block of 8 bits is some information to tell the IC how it should work for us. The MCP3008 is a little simpler than the MCP3002. This table shows what is needed :


Bit Value Explanation
SGL/DIFF
1/0
1 - To select Single Ended Mode (This is the mode we will use)
0 - To select Pseudo Differential Mode
D2/D1/D0
0/1
Channel Selection. Each of these bits put together gives the equivalent decimal channel number once converted

To decide on the channel we need to do a little binary conversion. This Table should Help you out with that :


D2 D1 D0 Channel
0
0
0
CH0
0
0
1
CH1
0
1
0
CH2
0
1
1
CH3
1
0
0
CH4
1
0
1
CH5
1
1
0
CH6
1
1
1
CH7


So the bits, in order, that we need are a four bit value that needs to shifted to the left by 4 to make it 8 bits. The first bit in 4 bit number is going to be 1 (SGL/DIFF). And therefore being the first bit in the four bits needed it will be 1000 which is converted to a decimal value of 8. No we need to add onto that the channel we decided to use. So using CH0 in my example it would be still be 1000. If say for example it was CH3 the four bit digit would be 1011. Now we shift it over to the left by 4 using the bit operator  <<. We get the function 
       (8 + channel) << 4

On the third 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 1, (8 + channel) << 4, 0 

Receiving Data

As you can see result receives the information that spi.xfer2[(1, (8 + channel) << 4, 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). Three blocks of 8 bits. The first block is ignored and is not relevant to the calculation even if it has anything in it. The second block holds 2 bits of the final code and the third 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. Which we just saw in sending data to the device.

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

The second block we see that only the last 2 bits are needed, though the device could send back more. This is position 1 in the list of result. Therefore result[1]. 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[1] & 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[2], 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 MCP3008 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.




No comments:

Post a Comment