Hello folks, in this tutorial we will see how to take analog input raspberry pi using the MCP3208 analog to digital converter IC. We will also understand using the using SPI and spi.xfer2([6| (channel << 2), channel << 6,0]) function usage.
Fig.1: Raspberry Pi Analog Input with MCP3208 Proteus Circuit
You may think what's going on here, in Arduino (if you're coming from Arduino) we just need to connect the analog sensor to the analog pin and one line of code "analogRead()" that's all. Well, here the problem is raspberry pi is a computer and it does not come with the analog sensor reading ability. Arduino has a built-in analog to digital converter (ADC) chip that automatically converts the incoming analog data to its digital form. In the case of raspberry pi, we need to use any external analog to digital conversion IC. I'm using MCP3208 for this tutorial but you can use any other alternative like MCP3204 or MCP3008.
Now let's build the circuit in proteus first using the using Fig.1. The components you need for the circuit:
Raspberry Pi 3
MCP3208
Potentiometer 1K/ 10K
The circuit is simple. I hope you find no difficulty building it. Now let's see the code:
import spidev
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
#SPI Setup
spi = spidev.SpiDev()
spi.open(0,0)
spi.max_speed_hz=1000000
#the chip select pin is connected to GPIO 12, depending on it the data will be read or not read
Most of the code is understandable if you're following along the series and there are comments to help. I'll focus on the analogRead function in the code.
The analogRead function takes "channel" as the input. We've to provide the channel number of the MCP32058 from which we will read the data and the sensor/ pot is connected to. In order to read data from a particular channel, we need to send a 3-byte data list to the ADC chip.
Fig.2: Send and Receive data from MCP 3208
What is the meaning of spi.xfer2([6| (channel >> 2),channel << 6,0])?
The MCP3208 chip expects 3 separate bytes of data in the format shown on Fig.2. Here the 3rd byte of data is not important. So, we can send 0 in decimal or "0000 0000" in binary. The 2nd, 1st, and 0th bit of 1st byte data and 7th, 6th bit of 2nd byte data is important. All the other bits are negligible.
The 2nd bit of 1st Byte data represents the "start bit", it needs to be set to 1.
The 1st bit of that byte represents the signal mode of the chip. You can leave it as one.
The 0th bit of first byte and 6th and 7th bit of 2nd byte data represents the channel number from which we are requesting data.
The MCP3208 has total of 8 channels starting from channel 0 to channel 7. So the binary combinations fro these channels become:
D2 D1 D0 Decimal
--- --- --- ---------
0 0 0 => 0
0 0 1 => 1
0 1 0 => 2
0 1 1 => 3
1 0 0 => 4
1 0 1 => 5
1 1 0 => 6
1 1 1 => 7
1 1 1 => 8
In this tutorial, I'm reading data from channel 5. So the binary for 5 is 101 and for the 1st byte of data it's D2 bit needs to be 1. So how do we do it?
1st parameter: Look closely at first parameter of the spi.xfer2() function. " 6 | (channel << 2) "
The binary value of 6 is 1 1 0.
channel >> 2 it means we need to right shift the binary value of the channel by 2.
So, 101 >> 2 or 0000 0101 >> 2 (converting it to 8 bit) gives us, 0000 0001
Now we need to perform OR operation.
So,
6 => 0000 0110
0000 0101 => 0000 0001
-------------------------------
output => 0000 0111
This way can set the first 3 bits of the 1st byte of data.
2nd parameter: channel << 6
We have sent the D2 bit of the channel, D1 and D0 bit is left to send.
channel << 6 means 0000 0101 << 6 and it gives us the 2nd byte as 0100 0000
and the 3rd parameter is zero.
When we send the data, the chip will send response back and it will be stored inside the adc variable. Now we need to decode this data. This variable adc is also a 3-byte data array.
In between the received 3 bytes, the 1st byte of data is not important. The 3rd byte contains the lower 8 bits of the data but the data is total 12 bits, the first 4 bits are stored inside the 2nd byte of the data. The 3rd, 2nd, 1st and the 0th bit.
Let's say the data transmitted from the chip is 3056 which is 1011 1111 0000 in binary. Now lets look at the data = ((adc[1]&15) << 8) + adc[2] line inside the analogRead function.
adc[1] & 15 means
x x x x 1011
0 0 0 0 1111
--------------
0 0 0 0 1011 now we need to left shift it by 8. It gives us 1011 0000 0000. The binary value of adc[2] means 1111 0000. Adding these two value means performing OR operation between these two that gives us
1011 0000 0000
0000 1111 0000
-------------------
1011 1111 0000
We've the decoded adc value inside the data variable. We can directly use it or calculate the value of voltage using the formula, adc_value = 3.3 * ( data / (2** 12 -1)) # vref*(value/(2**bitdepth-1)) .
Inside the while loop we first need to turn the CS pin to low and after getting the value we need to set it back to HIGH.
If you have any difficulty, you can always watch my video on youtube.
Leave a Comment