Interfacing an SPI ADC (MCP3008) chip to the Raspberry Pi using C++ (spidev)

In this entry I will demonstrate how to interface the MCP3008; an SPI-based analog to digital converter (ADC) integrated chip, to the Raspberry Pi. This enables  the Raspberry Pi to interpret analog voltages that are in turn typically emitted by analog-based sensors to reflect a measure of  a physical characteristic such as acceleration, light intensity or temperature. We will start by briefly examining the SPI interface.

Figure 1. 3 SPI slave devices connected to a single SPI master device. In this case a total of 3+n - > 3+3= 6 wires are required. Image acquired from here

Figure 1. 3 SPI slave devices connected to a single SPI master device. In this case a total of 3+n – > 3+3= 6 wires are required. Image acquired from here

A gentle introduction to the Serial Peripheral Interface (SPI) 

The Serial Peripheral Interface (SPI) is a communication bus that is used to interface one or more slave peripheral integrated circuits (ICs) to a single master SPI device; usually a microcontroller or microprocessor of some sort. Many SPI  Peripheral ICs exist. They include, analog to digital converters (ADC), digital to analog converters (DAC), general purpose input/output (GPIO) expansion ICs, temperature sensing ICs, accelerometers and many more.  In this regards the SPI bus is similar to the I2C bus. the SPI’s main advantage over the I2C bus is that the SPI bus speeds can be very fast; 10Mbps is not unusual and maximum speeds can go as high as the hardware (master controller, slave peripheral, and printed circuit board traces connecting them) will go. Speeds that exceed 50Mbps are not unusual.

Having said that, the main disadvantage of the SPI bus w.r.t the I2C bus is the numbers of wires required by the bus. The I2C bus is a ’2-wire’ bus that theoretically can be used to connect up to 127 devices, and significantly more if a 9-bit addressing scheme is used instead of the classical 7-bit address. The SPI bus is a ’3+n wire’ bus. where ‘n’ is the number of slave devices attached to the master SPI device. For example in the example shown in Figure 1, a total of 6 wires are required by the SPI bus and six corresponding pins required on the master SPI device to interface the 3 slave SPI devices to the master SPI controller. This means that not only is the number of wires on an SPI bus larger than on an I2C bus, but that the number of wires continue to increase linearly as we add more slave devices on to the bus. Notice that slave SPI devices almost always require 4 pins to attach themselves to the SPI bus.

The 3 SPI wires shared by all devices on the SPI  bus are:

  • Master in slave out (MISO). Data is moved from slave to master on this wire.
  • Master out slave in (MOSI). Data is moved from master to slave on this wire.
  • Serial clock (SCLK). This clock is always generated by the master controller and is used to synchronize the transmission of data between devices on the bus.

In addition to these wires we have ‘n’ wires for ‘n’ slave devices on the bus. Each one of these wires carries the chip select signal (SS or CS) for its respective device. Only one slave device can have its chip select signal asserted by the master controller at a time.

The operation of the SPI bus is conceptually simple. Both the master controller and each slave device contain a shift register. When the chip select signal of a slave device is asserted (usually by being pulled low), the MISO, MOSI wires are used to connect its shift register with that of the master device. Clock pulses are then generated (by the master device)  to shift data between the two shift registers enabling communication. In this sense the read and write operation are combined. For example by shifting the contents of the master device shift register to  that of the slave device, we are  also shifting the data in the slave device shift register to that of the master.

Finally, there are 4 different SPI modes that can be used. Each mode defines a particular clock phase (CPHA) and polarity (CPOL) with respect to the data. For the purpose of this tutorial we will be utilizing SPI mode 0 which is also known as mode (0,0) or mode (CPHA=0,CPOL=0).

To learn more about the SPI bus, I refer the reader to these excellent resources:

The MCP3008 SPI ADC chip

The MCP3008 chip is an SPI based analogue to digital converter (ADC). It has 8 analog input channels that can be configured for single ended and differential ADC conversions. The MCP3008 is a 10-bit ADC  that can convert  up to 200 kilo samples per second (200ksps) (@ 5V!!). The MCP3008 comes in 28 PDIP and SOIC packages. A pinout is provided in Figure 3. The datasheet for the MCP3008 can be can be downloaded from here.

Figure 3. Pinout of the MCP3008 IC

Figure 3. Pinout of the MCP3008 IC Taken form the MCP 3008 datasheet

Typically the VDD pin is connected to  3.3V power. The AGND and DGND pins can be connected directly to the ground reference point. The VREF pin is the reference voltage which is the largest possible voltage that the ADC can interpret. In our scenario we will connect the VREF pin to 3.3V (same as VDD). So if 3.3V was sampled on any of the ADC’s channels it would be interpreted as the maximum digital value that can be represented by this 10-bit ADC i.e. 2^10 – 1 = 1023. Similarly the smallest analog voltage that the ADC can detect (also known as the ‘LSB size’) is VREF/1024. Which in our case is 3.3V/1024= 3.22mV and represents a digital value of 1. The equation that converts between the analog voltage and its digital interpretation is given by “Digital output code = 1024*VIN/VREF”; where VIN is the analog input voltage and VREF is the reference voltage.

A complete SPI transaction for the MCP3008 (SPI mode 0) is depicted in Figure 4. The complete transaction consists of 3 bytes being transmitted from master (Raspberry Pi) to slave (MCP3008) and 3 bytes transmitted from slave to master. Recall that due to the nature of the shift register  operation of the SPI bus, shifting 3 bytes into the slave device (writing to the slave MCP3008) will by default cause the 3 bytes in the slave device to be shifted out into the master device (Raspberry Pi).

Figure 4. Complete SPI transaction for the MCP3008. Diagram taken from MCP 3008 Datasheet.

Figure 4. Complete SPI transaction for the MCP3008. Diagram taken from MCP 3008 Datasheet.

  1. Raspberry Pi asserts the chip select signal connected to the MCP3008 (CS0 in our case) by setting it to 0V. This is typically taken care of internally by the spidev driver whenever the proper ioctl() function is called.
  2. Raspberry Pi sends a byte containing a value of ’1′ to the MCP3008. This is a start bit. At the same time the MCP3008 sends back a ‘don’t care’ byte to the Raspberry Pi.
  3. Raspberry Pi then sends a second byte whose most significant nibble (SGL/DIFF,D2,D1 & D0 bits) indicate the channel that we want to convert and whether we want  single-ended or differential conversion (See Figure 5). For example if this nibble is “1000″, the conversion will be single-ended and take place on channel 0 (CH0 pin). The least significant nibble is sent as ‘don’t care’. At the same time, the MCP3008 sends back the two most significant bits of the digital value (result) of the conversion (bits 8 and 9).
  4. Raspberry Pi sends another  ‘don’t care’ byte to the MCP3008. At the same time the MCP3008 send back a byte containing bits 7 through 0 0f the digital value (result) of the conversion.
  5. The Raspberry Pi then merges bits 8 & 9 from the second received byte with bits 7  through 0 from the third received byte to create the 10-bit digital value resulting from the conversion.
Figure 5. Configuring the ADC conversion

Figure 5. Configuring the ADC conversion. Table taken from MCP3008 datasheet.

 

Connecting the Raspberry Pi to the MCP3008.

So let’s connect  the Raspberry Pi to the MCP3008 ! This can be done as shown in Figure 6.

Figure 6. Connecting the Raspberry Pi to the MCP3008

Figure 6. Connecting the Raspberry Pi to the MCP3008

The connections between the Raspberry Pi and the other parts can be made via Male-to Female jumper wires or via one of Adafruit’s Pi cobbler kits (ver1  or ver2).  Note that in this particular scenario, I opted for attaching the analog input coming from a potentiometer to channel 0 (CH0 pin). Also the SPI peripheral on the Raspberry Pi has only two chip selects (CS0,CS1 pins) and can therefore only be used to attach a maximum of two SPI slave devices to the Raspberry Pi. We chose the CS0 pin.

Enabling Spidev on the Raspberry Pi

The next step is to enable the spidev interface on the Raspberry Pi :

  • Turn on the Raspberry Pi and connect it to the network.
  • Log in to the Raspberry Pi remotely over SSH.
  • Open the raspi-black-list.conf  file using the following command : “sudo nano /etc/modprobe.d/raspi-blacklist.conf”
  • Comment out the “blacklist spi-bcm2708” entry by putting a hash # sign in front of it. So it looks like “#blacklist spi-bcm2708”.
  • Type “sudo reboot”. This should cause the Raspberry Pi to reboot. Which will terminate the SSH session.
  • Start a new SSH session and type “ls /dev/spidev*” If you see the output displayed in Figure 7, then the SPI driver (spidev) has been successfully enabled on the Raspberry Pi.
Figure 7. spidev enabled

Figure 7. spidev enabled

Note how there are two spidev devices displayed in Figure 7. The first number refers to the SPI peripheral which in both cases is 0, as there is only one SPI device available to us on the Raspberry Pi. The second number represents the chip select i.e. spidev0.0 for CS0 and spidev0.1 for CS1 . In this scenario we are only interested in spidev0.0 since we will be using CS0.

C++ Program to control the MCP3008 from the Raspberry Pi

So the next step is to write C++ class / program that allows us to communicate with the MCP3008. We called this class “mcp3008Spi”. This c++ class relies heavily on the spidev user space interface. Although it has “mcp3008″ in its name, it was designed to be easily modified to work with other SPI chips as well. The “mcp3008Spi”‘s class definition is provided below. The class consists of four variables, two constructors, one destructor, spiOpen() that opens and configures the spidev interface, spiClose() that closes the spidev interface and a “spiWriteRead()” function that transfers data between master and slave devices.

/***********************************************************************
 * This header file contains the mcp3008Spi class definition. 
 * Its main purpose is to communicate with the MCP3008 chip using 
 * the userspace spidev facility. 
 * The class contains four variables:
 * mode        -> defines the SPI mode used. In our case it is SPI_MODE_0. 
 * bitsPerWord -> defines the bit width of the data transmitted. 
 * 		  This is normally 8. Experimentation with other values 
 * 		  didn't work for me
 * speed       -> Bus speed or SPI clock frequency. According to 
 *                https://projects.drogon.net/understanding-spi-on-the-raspberry-pi/
 * 	          It can be only 0.5, 1, 2, 4, 8, 16, 32 MHz. 
 *                Will use 1MHz for now and test it further.
 * spifd       -> file descriptor for the SPI device
 * 
 * The class contains two constructors that initialize the above 
 * variables and then open the appropriate spidev device using spiOpen().
 * The class contains one destructor that automatically closes the spidev
 * device when object is destroyed by calling spiClose().
 * The spiWriteRead() function sends the data "data" of length "length" 
 * to the spidevice and at the same time receives data of the same length. 
 * Resulting data is stored in the "data" variable after the function call.
 * ****************************************************************************/
#ifndef MCP3008SPI_H
	#define MCP3008SPI_H
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string>
#include <iostream>

class mcp3008Spi{

public:
	mcp3008Spi();
	mcp3008Spi(std::string devspi, unsigned char spiMode, unsigned int spiSpeed, unsigned char spibitsPerWord);
	~mcp3008Spi();
	int spiWriteRead( unsigned char *data, int length);

private:
	unsigned char mode;
	unsigned char bitsPerWord;
	unsigned int speed;
	int spifd;

	int spiOpen(std::string devspi);
	int spiClose();

};

#endif

The constructors initialize the variables and then call the “spiOpen()” function to initialize the spidev interface. The destructor is typically called when the “mcp3008Spi” object is about to be destroyed and calls the spiClose() function to close the spidev interface just before the object is destroyed.

The spiWriteRead() function can be used to send/receive SPI transactions made of one or multiple bytes and should be compatible for use with other SPI devices. The member function definitions are provided below.

#include "mcp3008Spi.h"
using namespace std;
/**********************************************************
 * spiOpen() :function is called by the constructor.
 * It is responsible for opening the spidev device 
 * "devspi" and then setting up the spidev interface.
 * private member variables are used to configure spidev.
 * They must be set appropriately by constructor before calling
 * this function. 
 * *********************************************************/
int mcp3008Spi::spiOpen(std::string devspi){
	int statusVal = -1;
	this->spifd = open(devspi.c_str(), O_RDWR);
	if(this->spifd < 0){
		perror("could not open SPI device");
		exit(1);
	}

	statusVal = ioctl (this->spifd, SPI_IOC_WR_MODE, &(this->mode));
	if(statusVal < 0){
		perror("Could not set SPIMode (WR)...ioctl fail");
		exit(1);
	}

	statusVal = ioctl (this->spifd, SPI_IOC_RD_MODE, &(this->mode)); 
	if(statusVal < 0) {
	  perror("Could not set SPIMode (RD)...ioctl fail");
	  exit(1);
	}

	statusVal = ioctl (this->spifd, SPI_IOC_WR_BITS_PER_WORD, &(this->bitsPerWord));
	if(statusVal < 0) {
	  perror("Could not set SPI bitsPerWord (WR)...ioctl fail");
	  exit(1);
	}

	statusVal = ioctl (this->spifd, SPI_IOC_RD_BITS_PER_WORD, &(this->bitsPerWord));
	if(statusVal < 0) {
	  perror("Could not set SPI bitsPerWord(RD)...ioctl fail");
	  exit(1);
	} 	

	statusVal = ioctl (this->spifd, SPI_IOC_WR_MAX_SPEED_HZ, &(this->speed)); 	
	if(statusVal < 0) {
	  perror("Could not set SPI speed (WR)...ioctl fail");
	  exit(1);
	} 

	statusVal = ioctl (this->spifd, SPI_IOC_RD_MAX_SPEED_HZ, &(this->speed)); 	
	if(statusVal < 0) {
	  perror("Could not set SPI speed (RD)...ioctl fail");
	  exit(1);
	}
	return statusVal;
}

/***********************************************************
 * spiClose(): Responsible for closing the spidev interface.
 * Called in destructor
 * *********************************************************/

int mcp3008Spi::spiClose(){
	int statusVal = -1;
	statusVal = close(this->spifd);
    	if(statusVal < 0) {
	  perror("Could not close SPI device");
	  exit(1);
	}
	return statusVal;
}

/********************************************************************
 * This function writes data "data" of length "length" to the spidev
 * device. Data shifted in from the spidev device is saved back into 
 * "data". 
 * ******************************************************************/
int mcp3008Spi::spiWriteRead( unsigned char *data, int length){

  struct spi_ioc_transfer spi[length];
  int i = 0; 
  int retVal = -1;  

// one spi transfer for each byte

  for (i = 0 ; i < length ; i++){

	spi[i].tx_buf        = (unsigned long)(data + i); // transmit from "data"
	spi[i].rx_buf        = (unsigned long)(data + i) ; // receive into "data"
	spi[i].len           = sizeof(*(data + i)) ;
	spi[i].delay_usecs   = 0 ; 
	spi[i].speed_hz      = this->speed ;
	spi[i].bits_per_word = this->bitsPerWord ;
	spi[i].cs_change = 0;
}

 retVal = ioctl (this->spifd, SPI_IOC_MESSAGE(length), &spi) ;

 if(retVal < 0){
	perror("Problem transmitting spi data..ioctl");
	exit(1);
 }

return retVal;

}

/*************************************************
 * Default constructor. Set member variables to
 * default values and then call spiOpen()
 * ***********************************************/

mcp3008Spi::mcp3008Spi(){
	this->mode = SPI_MODE_0 ; 
	this->bitsPerWord = 8;
	this->speed = 1000000;
	this->spifd = -1;

	this->spiOpen(std::string("/dev/spidev0.0"));

	}

/*************************************************
 * overloaded constructor. let user set member variables to
 * and then call spiOpen()
 * ***********************************************/
mcp3008Spi::mcp3008Spi(std::string devspi, unsigned char spiMode, unsigned int spiSpeed, unsigned char spibitsPerWord){
	this->mode = spiMode ; 
	this->bitsPerWord = spibitsPerWord;
	this->speed = spiSpeed;
	this->spifd = -1;

	this->spiOpen(devspi);

}

/**********************************************
 * Destructor: calls spiClose()
 * ********************************************/
mcp3008Spi::~mcp3008Spi(){
	this->spiClose();
}

Finally  we write an application to test the “mcp3008Spi” class. The application initializes an “mcp3008Spi” object. sends  3 bytes through the spidev interface to configure the conversion for single ended conversion on channel 0. 3 bytes are transmitted back at the same time. The last two contain the digital result of the conversion.

/***********************************************************************
 * mcp3008SpiTest.cpp. Sample program that tests the mcp3008Spi class.
 * an mcp3008Spi class object (a2d) is created. the a2d object is instantiated
 * using the overloaded constructor. which opens the spidev0.0 device with 
 * SPI_MODE_0 (MODE 0) (defined in linux/spi/spidev.h), speed = 1MHz &
 * bitsPerWord=8.
 * 
 * call the spiWriteRead function on the a2d object 20 times. Each time make sure
 * that conversion is configured for single ended conversion on CH0
 * i.e. transmit ->  byte1 = 0b00000001 (start bit)
 *                   byte2 = 0b1000000  (SGL/DIF = 1, D2=D1=D0=0)
 *                   byte3 = 0b00000000  (Don't care)
 *      receive  ->  byte1 = junk
 *                   byte2 = junk + b8 + b9
 *                   byte3 = b7 - b0
 *     
 * after conversion must merge data[1] and data[2] to get final result 
 * 
 * 
 * 
 * *********************************************************************/
#include "mcp3008Spi.h"

using namespace std;

int main(void)
{
	mcp3008Spi a2d("/dev/spidev0.0", SPI_MODE_0, 1000000, 8);
	int i = 20;
        int a2dVal = 0;	
	int a2dChannel = 0;
        unsigned char data[3];

	while(i > 0)
	{
		data[0] = 1;  //  first byte transmitted -> start bit
		data[1] = 0b10000000 |( ((a2dChannel & 7) << 4)); // second byte transmitted -> (SGL/DIF = 1, D2=D1=D0=0)
		data[2] = 0; // third byte transmitted....don't care

		a2d.spiWriteRead(data, sizeof(data) );

		a2dVal = 0;
                a2dVal = (data[1]<< 8) & 0b1100000000; //merge data[1] & data[2] to get result
                a2dVal |=  (data[2] & 0xff);
		sleep(1);
		cout << "The Result is: " << a2dVal << endl;
		i--;
	}
	return 0;
}

Save the above three files as mcp3008Spi.h, mcp3008Spi.cpp & mcp3008SpiTest.cpp respectively and put them in the same directory on the Raspberry Pi; say a directory called spitest. Then build the application natively on the Raspberry Pi using the following command “ g++ -Wall -o OutBin  mcp3008Spi.cpp mcp3008SpiTest.cpp”. Run the binary “OutBin” as root i.e. “sudo ./OutBin“. Start rotating the potentiometer and the digital values printed should vary within a range of 0-1023 as shown in Figure 8. Congratulations! You got the Raspberry Pi and MCP3008 to communicate! The Raspberry Pi can now be used to read/interpret analog data.

Figure 8. Digital result of MCP3008 conversion printed to console as potentiometer needle rotated from one extreme to the other

Figure 8. Digital result of MCP3008 conversion printed to console as potentiometer needle rotated from one extreme to the other

The code shown in this blog is available for download (git) here.

A note about sampling speed:

The maximum sampling frequency for the MCP3008 Chip is 200ksps at 5V and 75Ksps at 2.7V. At 3.3V, I doubt that it would go above 100Ksps. Furthermore because the spidev interface used here is a Linux userspace interface with slow access to kernel space and non-deterministic timing, expect significantly lower sample rates.

There really is no way to directly control the sampling speed. You could speed up/slow down the speed of the spi clock (in the code example set to 1MHz…Maximum is 1.35MHz @ 2.7V) and call the spiWriteRead function at regular intervals using functions such as usleep or POSIX timers but I’m not sure what kind of sample rates you’ll get without doing some experimentation.

At maximum spi clock speed and calling spiWriteRead as fast as possible I doubt you’d be able to achieve anything near 20ksps. There will also be synchronicity issues i.e. the delay between consecutive samples could exhibit a large variance due to the non-deterministic nature of the Linux Kernel. Remember….Linux is NOT a real-time operating system!!!

This solution is meant for applications where real-time constraints such as sampling speed requirements are very modest or even non-existent.

If you have strict sampling requirements, consider one of the following approaches:

  • Access the SPI registers directly by mmaping into /dev/mem or via the bcm2835 library.
  • Bitbang SPI via GPIO or use a Parallel ADC connected to the RPI’s GPIOs. The RPI GPIOs can be controlled by many different methods, one of which can achieve  20MHz + toggling speed (also mmaping into /dev/mem  or via the bcm2835 library but for GPIOs)
  • Write your own custom/optimized ADC device driver. Have a look at the 24th (June 2014) edition of the MagPi Magazine. The first article explains how to write a Linux driver for a 6-bit 10MSPS ADC and use it in conjunction with GNUplot to plot the waveforms.
  • Use a microcontroller with a decent built-in ADC and generous amounts of memory for buffering….and maybe a DMA unit e.g. STM32F0/1/2/3/4, TIVA C , PIC32MX, PIC32MZ, e.t.c. The micro can then transmit ADC data to the RPI over UART, SPI or I2C.

For speech related tasks, I recommend using a USB microphone with the PortAudio Audio I/O library. Its the same library that the Audacity software uses. Unfortunately I’ve never used PortAudio..but it is well documented and the main PortAudio site hosts a nifty tutorial.

Finally, for those looking for a more robust ADC setup for the Raspberry Pi, there are many ADC add-on boards that utilize the MCP3008/MCP3208 SPI ADC’s. One that I highly recommend is the BAART ADC-8.

This entry was posted in Raspberry Pi, Raspberry Pi Peripherals. Bookmark the permalink.

76 Responses to Interfacing an SPI ADC (MCP3008) chip to the Raspberry Pi using C++ (spidev)

  1. Pingback: Interfacing an SPI ADC (MCP30008) chip to the Raspberry Pi using C++ (spidev) @Raspberry_Pi #piday #raspberrypi « adafruit industries blog

  2. Abhiram says:

    I can’t thank you enough mate!
    Btw what do you think will change if I am using a 12bit controller? (mcp 3208 in my case?). I just have mutipley the input and reference ratio with the maximum value right? (2^12-1)

    • halherta says:

      Abhiram, I haven’t used the mcp3208 but the procedure seems to be very similar.

      • Abhiram says:

        @halherta, the other change would be the output from the adc would be the most significant 4 bits in response( A 10 bit ADC would return only 2 bits) followed by the remaining byte in the next response.

  3. Andrey says:

    How stable are the values returned by MCP3008? I mean if you leave the potentiometer somewhere in the middle and do not touch it, what kind of deviation would you get between these 20 results?

    • halherta says:

      Andrey the results are pretty stable with some minor deviation…the kind that you’d get from a microcontroller 10-bit ADC. Stability can be increased a bit by using coupling caps and a PCB instead of a breadboard. Also grounding unused channels might help

      • Andrey says:

        I guess I am interested exactly in what kind of deviation is expected from a microcontroller 10-bit ADC on a breadboard :) And what kind could be achieved on a PCB with a cap somewhere.

        • halherta says:

          Andrey, Without a more sophisticated setup I’m unable to provide you with much more detail…I’m seeing a variation of 1-3 LSB but this is not consistent and relies on many things from quantization errors (1LSB DNL/INL) to noise introduced by breadboards and lack of decoupling caps and an potentially unstable power supply…..(Due to the power draw of the RPi SOC the 3.3V supply is not super stable all the time). Heck even the potentiometer itself could cause these minor variations even when it is stationary. If you really want a very high degree of accuracy I suggest taking a look at the MCP3208. It is a 12-bit Sigma Delta ADC which will provide more accuracy than the MCP3008 10-bit Successive approximation ADC. One other thing, the maximum sampling rate of the MCP3208 is 100Ksps while the maximum sampling rate of the MCP3008 is 200Ksps…so If you want speed go with the MCP3008….if you want accuracy go with the MCP3208….Or even better buy both and experiment with them…The mcp3008spi library could be easily adapted to deal with the MCP3208 chip as well

  4. Gaurav Holey says:

    Hello,

    This is gaurav. I am trying to develop a weighing machine application. I am considering raspberry pi for the same. i have an spi 24 bit ADC AD7730. How things will change for this scenario and how many spi controllers are available in Raspberry PI and no of GPIO available for interfacing LCD and other peripherals?

    Please revert.. i am looking forward to your reply.
    Thanking you in anticipation.
    Regards,
    Gaurav Holey

    • halherta says:

      Guarav,
      Take a look at this link for info on the Raspberry Pi’s I/O capabilities. Basically you have a basic two pin UART, a single I2C peripheral and a single SPI peripheral with two chip selects (one SPI bus connects to two SPI slave devices) and an additional 8 GPIO pins on a 26 pin header. Ofcourse if you’re not using /SPI/I2C/UART peripherals you can use their pins as GPIO as well.

      The mcp3008Spi class provided in the article is very generic and I should’ve called it something more generic…It should take care of the underlying SPI communication. You simply need to figure out what data to write and read via the “spiWriteRead” function. To figure this out…have a look at the AD7730 datasheet.

      The Raspberry Pi has a DSI interface to drive an LCD…but the LCD has not been released yet. You could however interface an LCD over the SPI/I2C interface….for use a GPIO expander (MCP23017 (I2C)/MCP23S17 (SPI)) to interface to a text based LCD or monochrome 128x64pixel LCD

  5. Mark says:

    Great post, thank you!

    What is the maximum sample rate that you can achieve – can you get anywhere near the 200 ksps/s the chip is designed for?

    Thanks,
    Mark

    • halherta says:

      Mark good question…I haven’t really pushed the mcp3008 to the limit but it would be interesting to find out how fast it can go….Since we are not using an RTOS i doubt that the you can indeed achieve the 200ksps with the raspberry Pi but who knows..If I get as chance to test it I will post the results.

    • In the electrical specification (page 3 of the PDF of the datasheet) shows that to get fsample=200 ksps you need a fclock=18*fsample, this is 3.6 MHz, no problem for the clocks of the Raspberry Pi, other problem is if the Raspberry Pi can polling enough fast to really acquire the samples, I have several ideas to make a osciloscope, I think if the Raspberry don’t have any other task and using the GPU (OpenGL + shaders) to display, the raspberry can polling the data enough fast to make a portable osciloscope, but this is in theory, I need to experiment ;).

      Greetings from Mexico.

      • halherta says:

        Federico, The Raspberry Pi hardware is not the bottleneck….the user space spidev driver however may be a bottleneck. If it indeed is, the best way around it would be to write a custom linux device driver for the mcp3008…this way you’re guaranteed to get the best performance possible.

  6. Greg says:

    Thanks – great post. Ive no idea what all the code does but it works :)

    I struggled for ages trying to the Adafruit code working with the mcp3008 but no luck.

    Yours worked within mins! :)

  7. Vitor Christo says:

    Dear friend thank you very much for this clear explanation.
    I need your help, if I do not disturb you, I’am developing a system to read an CCD3041 (Fireshield 4M Pixel b&w ccd), this project will be applied on amateur astronomical telescope, (I plane to share this project) I created a hardware interface, very simple and I need now to read the analog pixel, from ccd via A2D circuit.
    The sample rate is 50khz, I need to synchronize the ADC read out. The hardware gives the line ( called ARC – Analog Ready to Conversion). I`am trying to use this line to activate an GPIO interrupt, but the results are showed slowly.
    I Know the MCP3008 are able to make 200ksps, can you give me some suggestion, to implement the synchronized ADC read, from this ?
    Thank you …
    Vitor

    • halherta says:

      Vitor,
      Based on what I understood from your project,you may have to create a Linux device driver (kernel space programming) for the mcp3008 to achieve the desired throughput & synchronicity. The user space spidev (what i used) might not be sufficient…especially on the synchronicity front.

      Alternatively you could use the built-in A2D built into a microcontroller (STM32F0/F1/F2/F3/F4 or TIVA C) and have that microcontroller communicate the results to the Raspberry Pi.

      Additionally I’d recommend taking a look at the Beaglebone Black. In addition to the Cortex A8, it has two 200MHz programmable real-time CPUs (PRUs) that might enable you to satisfy your requirements.

  8. Muddassir Chaudhry says:

    I am working on project,using MCP3008 & it works fine for one channel but more than one Analog input makes it all go crazy. PLease help

    Ur advice will be highly appreciated.

    - Muhammad Muddassir

    • halherta says:

      Muddassir, I must say i haven’t tried multiple channels…..can you please explain in detail what you mean by more than one channel ? i.e. how did you attempt multichannel?

      how are you powering the MCP3008? VREF needs to be super stable. Using the RPi’s power supply is simply not sufficient as it can fluctuate as the RPi Soc is put under load. Attaching many USB peripherals to the RPi without a powered hub could also cause this.

      Take a look at this discussion

      • Muddassir Chaudhry says:

        Thanx for such a prompt reply. I later on fixed the problem by configuring the driver spidev and also a faulty wire was causing all the trouble. I am now using 5 flex sensors as analog inputs and ADC MCP3008 is working fine. Although, i have to work on making Vref super stable.

        Thanks for the advice.

        Regards

        Muddassir

  9. Pingback: Using The SPI Interface « Raspberry Pi Projects

  10. Pingback: Creating A Class « Raspberry Pi Projects

  11. Hans Schulz says:

    Just want to mention that I just finished writing a modularized driver for the mcp3304 adc (which looks like its pin-compatible to the mcp3008 but has 13 bits). My driver comes in place of the spidev driver and provides 8 devide nodes: /dev/adc0.0 through /dev/adc0.7, representing each of the eight analog inputs of the chip. Every device node can be opened and read by multiple processes. The application can open() the device with a flag to select differential or single ended conversion per channel. At load time of the driver (with insmod/modprobe) you may specify the clock frequency for spi bus. This driver pretty much simplfies the application program, placing all chip-specific burden in the protocol driver (as it should…).
    I’m now working on an article about the project, including the associated hardware.
    I would be happy to share my ideas and code for those interested.
    Note 1: development and testing has only been done on the RaspberryPi (3.6.11+ kernel) but looks good so far, also regarding multi-channel, multi tasking operation.
    Note 2: you need to recompile the kernel because the configuration must be changed to replace spidev by spi-mcp3304.
    Note 3: the driver supports multiple adc’s as long as you have cs’s available on your spi-bus.

  12. halherta says:

    Hans, That’s awesome ! Please keep me posted! I look forwards to read your article/ instructions on using your spi-mcp3304 adc linux device driver !

  13. Kostas says:

    Hi! Very interesting circuit. What about if I want to get the same sampling rate with a 12bit resolution? Is there any alternative compatible with the code chip?

    Also, which type of Raspberry Pi (A or B or both) works with this code?

    • halherta says:

      Kostas for a 12-bit ADC try the mcp3208. the code will be almost identical…The exception is the second received byte… youll have to extract the four most significant bits instead of two…everything else is the same. I believe that the SPI pinout on both the model A & B is the same, so this should work on both RPi models.

      • Prageeth says:

        Got it working nicely for MCP3208 with little modification to test file as suggested by @halherta.

        unsigned char data[4];

        while (i > 0) {
        a2dVal0 = 0;
        a2dChannel = 0;
        data[0] = 0b00000001; // first byte transmitted -> start bit
        data[1] = 0b10000000 | (((a2dChannel & 7) < (SGL/DIF = 1, D2=D1=D0=0)
        data[2] = 0; // third byte transmitted….don’t care
        data[3] = 0; // fourth byte transmitted….don’t care

        a2d.spiWriteRead(data, sizeof(data));

        a2dVal0 = 0;
        a2dVal0 = (data[1] << 10) & 0b110000000000; //merge data[1] & data[2] & data[3] to get result
        a2dVal0 |= data[2] <> 6;

  14. Jason says:

    Nice! I sat down to write this code myself, but decided to give Giggle a shot and whatdya know! Why re-invent the wheel? Great head start for my project, thanks a ton!

  15. Pingback: Accessing both SPI ports on the raspberry pi using python | esologic.com

  16. Eshan Gray says:

    Excellent code. Can I ask is it possible to get this in C? I just need it to interface with a very specific system that only uses C. Otherwise, excellent and very helpful. :)

    • halherta says:

      Eshan, porting this code to C should be easy enough for someone who is knowledgeable in c & c++. Basically redefine the class member methods as separate functions…the constructor in C++ could be called spiInit() and the spiReadWrite() member method could be defined as a function e.t.c. You can take a look at WiringPi’s source code which implements SPI in C. Or just use the WiringPi library. Many examples exist on the web

  17. TLuong says:

    I am would like to use the Raspberry Pi to capture audio input (20-20,000 Hz) from a microphone and perform DSP on it. Wonder if the MCP3008 ADC and Raspberry Pi are suitable for this task? Thank you.

    • halherta says:

      I’d recommend using a USB microphone instead of an mcp3008 and use the portaudio library to access raw audio data via the Linux. But I really don’t know too much about this approach so please investigate. Using mcp3008 with spidev (user space spi interface under linux) as i did here is not ‘real-time’ enough to capture raw audio data properly in my opinion.

  18. Wendy says:

    Can you advise please wich parameters i need to change in your program if i want to use ADC 0834. Resolution 8 bits, fclock=250 Khz,4 channel. Thanks in advance.

  19. LS says:

    Hi,
    This is excellent and my mcp3008 is working.

    Anyone can give me a brief ideal how to sense DIFF instead of SGL? I am having a diff signal sensor.

    After reading the manual, some websites, etc. line 37 might be crucial?

    data[1] = 0b10000000 |( ((a2dChannel & 7) << 4));

    How about setting this to 0b00000000 ?

    But then again, do I need to read ch0 and ch1 for that?

    Best regards and Thank You for the great introduction.

  20. My write-up on using a MCP3002 or MCP3202 as an audio-frequency oscilloscope and spectrum analyzer is here:
    http://zunzun.com/downloads/scope.html

    with photos and screen shots. This should work unchanged for a MCP3008.

    Please email me if you have any questions.

    James Phillips
    zunzun@zunzun.com

    • halherta says:

      James, thanks for sharing! very informative and useful. BTW just out of curiosity what sort of maximum sampling rate does the Pi-based scope support ?

  21. Andrey says:

    Hello, thnx for this article.
    Did you tried to get 200KSPS from this ADC on Rasp?
    I’ve changed freq parameter in your example to 10000000 and got only ~18KSPS of stable signal.

    How to get 100-200KSPS from C++ code?

    With Best Regards,
    Andrey
    ammog@yandex.ru

    • halherta says:

      Andrey Your best bet would be to write a linux device driver for the chip and use the DMA unit. You can also try mmaping into /dev/mem from user space and accessing hardware registers directly. Checkout the source code that’s part of the Gertboard package for examples of mmaping into /dev/mem……

  22. Vadim says:

    Dear All hello!
    Please help me to modify this code to read 12-bit adc MCP3208….
    i tried code which Prageeth wrote above but there are errors in his code.

    • Zibeli says:

      Vadim (and others who may be interested),

      I also couldn’t sort out Prageeth’s changes, so instead I reworked the original test program code to: a) work with the 12 bit MCP3208, b) read and return the value for any of the MCP3208′s analog channels as specified by a command line argument, and c) present output more suited for use in a command line pipe (the single numerical value read per call).

      I’ll paste the revised code here, which I saved as file mcp3208SpiRead.cpp (in a directory with the original mcp3008Spi.h and mcp3008Spi.cpp files above) then compiled with “g++ -Wall -o mcp3208SpiRead mcp3008Spi.cpp mcp3208SpiRead.cpp“. It seems to be working fine thus far, though I am very far from skilled C++ programmer so definitely no guarantees!

      Hope this helps.


      /***********************************************************************
      * mcp3208SpiRead.cpp. Sample program that tests the mcp3208Spi class.
      *
      * Command syntax: ./mcp3208SpiRead Channel# (where Channel# is 0-7)
      *
      * Exits code 1 with syntax reminder if Channel# argument is invalid, otherwise
      * an mcp3208Spi class object (a2d) is created; the a2d object is instantiated
      * using the overloaded constructor, which opens the spidev0.0 device with
      * SPI_MODE_0 (MODE 0) (defined in linux/spi/spidev.h), speed = 1MHz &
      * bitsPerWord=8.
      *
      * Calls the spiWriteRead function on the a2d object, making sure that conversion is
      * configured for single ended conversion on CH0 using the a2d channel passed via
      * the command line argument.
      * i.e. transmit -> byte1 = 0b0000011c (5 0's, start bit, SGL/DIF=1, D2=channel msb)
      * byte2 = 0bcc000000 (D1,D0=channel middle and lsb)
      * byte3 = 0b00000000 (Don't care)
      * receive -> byte1 = junk
      * byte2 = junk + b11 - b8
      * byte3 = b7 - b0
      *
      * After conversion merges data[1] and data[2] to get final result, then echos
      * it to stdout and exits code 0.
      *
      *
      * *********************************************************************/
      #include "mcp3008Spi.h"

      using namespace std;

      int main(int argc, char* argv[])
      {
      // Check the calling arguments
      string ValidChannels("01234567");
      int argerror=0;
      if ( argc != 2 ) argerror=1;
      else if (string(argv[1]).length() != 1) argerror=1;
      else if (ValidChannels.find(string(argv[1]))==string::npos) argerror=1;
      if (argerror==1) {
      // Print usage reminder and exit code 1
      cerr << "Usage: " << argv[0] << " Channel# (where Channel# is 0-7)" <> 2)); // first byte transmitted -> start bit, SGL/DIF=1, D2
      data[1] = 0b00000000 |( ((a2dChannel & 3) < (D1,D0,don't care)
      data[2] = 0; // third byte transmitted....don't care

      a2d.spiWriteRead(data, sizeof(data) );

      a2dVal = 0;
      a2dVal = (data[1]<< 8) & 0b0000111100000000; //merge data[1] & data[2] to get result
      a2dVal |= (data[2] & 0xff);
      cout << a2dVal << endl;

      return 0;
      }

    • Zibeli says:

      Vadim (and others who may be interested),

      I also couldn’t sort out Prageeth’s changes, so instead I reworked the original test program code to: a) work with the 12 bit MCP3208, b) read and return the value for any of the MCP3208′s analog channels as specified by a command line argument, and c) present output more suited for use in a command line pipe (the single numerical value read per call).

      I’ll paste the revised code here, which I saved as file mcp3208SpiRead.cpp (in a directory with the original mcp3008Spi.h and mcp3008Spi.cpp files above) then compiled with “g++ -Wall -o mcp3208SpiRead mcp3008Spi.cpp mcp3208SpiRead.cpp“. It seems to be working fine thus far, though I am very far from skilled C++ programmer so definitely no guarantees!

      Hope this helps.


      /***********************************************************************
      * mcp3208SpiRead.cpp. Sample program that tests the mcp3208Spi class.
      *
      * Command syntax: ./mcp3208SpiRead Channel# (where Channel# is 0-7)
      *
      * Exits code 1 with syntax reminder if Channel# argument is invalid, otherwise
      * an mcp3208Spi class object (a2d) is created; the a2d object is instantiated
      * using the overloaded constructor, which opens the spidev0.0 device with
      * SPI_MODE_0 (MODE 0) (defined in linux/spi/spidev.h), speed = 1MHz &
      * bitsPerWord=8.
      *
      * Calls the spiWriteRead function on the a2d object, making sure that conversion is
      * configured for single ended conversion on CH0 using the a2d channel passed via
      * the command line argument.
      * i.e. transmit -> byte1 = 0b0000011c (5 0's, start bit, SGL/DIF=1, D2=channel msb)
      * byte2 = 0bcc000000 (D1,D0=channel middle and lsb)
      * byte3 = 0b00000000 (Don't care)
      * receive -> byte1 = junk
      * byte2 = junk + b11 - b8
      * byte3 = b7 - b0
      *
      * After conversion merges data[1] and data[2] to get final result, then echos
      * it to stdout and exits code 0.
      *
      *
      * *********************************************************************/
      #include "mcp3008Spi.h"

      using namespace std;

      int main(int argc, char* argv[])
      {
      // Check the calling arguments
      string ValidChannels("01234567");
      int argerror=0;
      if ( argc != 2 ) argerror=1;
      else if (string(argv[1]).length() != 1) argerror=1;
      else if (ValidChannels.find(string(argv[1]))==string::npos) argerror=1;
      if (argerror==1) {
      // Print usage reminder and exit code 1
      cerr << "Usage: " << argv[0] << " Channel# (where Channel# is 0-7)" <> 2)); // first byte transmitted -> start bit, SGL/DIF=1, D2
      data[1] = 0b00000000 |( ((a2dChannel & 3) < (D1,D0,don't care)
      data[2] = 0; // third byte transmitted....don't care

      a2d.spiWriteRead(data, sizeof(data) );

      a2dVal = 0;
      a2dVal = (data[1]<< 8) & 0b0000111100000000; //merge data[1] & data[2] to get result
      a2dVal |= (data[2] & 0xff);
      cout << a2dVal << endl;

      return 0;
      }

    • Zibeli says:

      Sorry, I can’t seem to paste the code here without it getting messed up, even when enclosed in tags. Anyone know a way to do it?

      • Steve Ferry says:

        Zibeli,

        I’m tearing my hair out here! I’d worked out from the MCP3208 datasheet that the initial values of data[0-1] had to be different from the MCP3008 exactly as you put in your code’s comments. however, I just cant get it to work. If I run the original 3008 code, I get values, albeit the wrong ones, but when I run my modified code, all I get is 0′s.
        The trouble with Prageeth’s code is that he doesn’t seem to have accounted for the fact that the start bit is two bits earlier, however he had the same problem as you in that the code isn’t all showing.
        Can you please email me a copy of your code and when I get my 3208 working, I’ll post a link to download it from my website.

        s.ferry@efftek-uk.com

        Regards, Steve.

      • Steve Ferry says:

        No more hair pulling; the relevant lines for the MCP3208 are

        data[0] = 6 | (((adchannel & 7 >>2);
        data[1] = 0 | (((adchannel & 3 <<6);
        data[2] = 0;
        a2d.spiWriteRead(data, sizeof(data));
        a2dval = 0;
        a2dval = (data[1] & 15) <<8;
        a2dval | = (data[2] & 255);

        Hope this helps.

  23. Eshan says:

    Hello. I seem to have run into a slight issue. After I connected up the device it seems to be fluctuating a lot and seems to be getting readings even though there is no input to the channels themselves i.e. no pots connected in this case. I cannot seem to see why this happens. Any suggestions?

    • Are the unused pins grounded? If they are unconnected, they can pick up stray RF energy. If I recall correctly, this is called “floating”, as in “Are the inputs floating or connected to something?”

      James

    • halherta says:

      Eshan, This is somewhat expected if the pins are floating (i.e. not connected to anything). Try connecting the input channel pin to either GND, 3.3V, a potentiometer or to a sensor of some sort. The input pins need to be connected to something in order for the ADC result to be valid and not fluctuate.

      • eshan says:

        I tried connecting all CH pins to ground but when I tried to read from CH0 the value still fluctuated wildly. Thank you for the suggestions though.

        • halherta says:

          OK so what was CHO connected to ??? A sensor a potentiometer or was it left unconnected ? How about the reference voltage pin vref ? Is that connected to a stable 3.3V supply ? Please give me more details regarding your setup…

          • Eshan says:

            I connected it to ground and while I was expecting a small fluctuation of a few points i was just surprised at what I got. The Vref was connected to the 3.3V pin on the Raspberry Pi. Any suggestions? Thank you for replying by the way.

  24. Steve Ferry says:

    Hi Guys. The company I am working for has a ball mill in Norway which keeps shearing the output shaft of the gearbox. The theory is that under load, the turntable is flexing, in turn bending the (180mm dia) output shaft, causing fatigue and inducing a stress fracture which eventually just goes BANG!

    My plan is to fit two orthogonal displacement probes and monitor the shaft centre – any bending will move the centre from its normal position. I was going to do this with an off the shelf USB-ADC and a PC but I’m up for a challenge.

    I’m now hoping to use a MCP3202, 2 channel, 12 bit ADC and a raspi-pi. The idea is that his will run permanently, taking samples and sending them to me via the internet – it’s too cold in Norway and the beer is way too expensive for me to be going back and forth just to take readings.

    The output shaft is about 60 rpm. I want about 200 samples per rev to construct a decent orbit plot hence I need to sample at 200hz BUT I need to read both channels as close to simultaneously as possible. I also want to collect a string of 2048 or 4096 samples so I can perform an FFT and determine bending frequency.

    How do I set the sampling speed? Should I open spidev0.0 at say 1Mhz as is the example and use a delay somewhere between reading both inputs so that my samples are at 200hz?

    I was programing in turbo C++ when Windows 3.1 was still in short trousers so it is my language of choice, python may as well be parceltongue to me! I will also be writing a VB6 program on the PC to do the orbit and FFT plots and would appreciate any suggestions how to get packets of data from the Pi to my PC – I can’t access the network in Norway to read the data, it can only be send out.

    Thanks in advance for any advice you can give.

    Steve.

    • halherta says:

      Steve, the RPi could probably pull it off. Andrey (one of the earlier commenters on this blog post) was able to get speeds of upto 18KSPS by setting the SPI clock frequency to 10 MHz. This is not impressive since the MCP32xx/MCP30xx are rated for sampling rates of 50/100Ksps (@2.7V) but this is the price paid for using a full blown OS like Linux and more importantly using ‘spidev’; a very generic user space method for accessing SPI in Linux. Also the length of the time interval between each conversion (whether on the same channel or another channel) will also exhibit some variation.

      But in your case, because you need only two channels sampled at only 200SPS… you’ll probably be OK even with an SPI clock of 1MHz.

      For exerting a degree on control over the sampling rate you can use POSIX timers or the function call usleep(). usleep is easier to use. Basically perform ADC conversion on both channels then call usleep(4000). This will put the process to sleep for 4ms. When it wakes up it can perform two more conversions and so on.

      As for accessing data you may want to use some basic BSD socket programming (yeah, I happen to be real old school) to move the data out of the Network in Norway to your PC. Basically your PC (preferably running Linux) can run a server program, and the RPi in Norway runs a client program sending this data out to your PC (server program). Ofcourse the IP address of your PC and the RPi have to be fixed. I’m sure there are other more ‘modern’ ways of doing this though that may be less time consuming.

      That’s a very interesting project you got there! Please let us know how it works out!

      • Steve Ferry says:

        Thanks for your help halherta.

        I think the usleep() method will be OK. In this instance, it does not matter if I sample at 195 hz or 205 hz etc as long as it is in the right ballpark. If in the future I need something a bit more frequency critical, I can calibrate it by applying a sine wave of a known frequency and adjusting the actual usleep() value until the fft shows the correct frequency.

        Regarding pushing the data out from Norway, I don’t want to send it directly to my PC, rather to a website which can be accessed by multiple pc’s worldwide. I guess I’m going to have to learn about MySQL etc also! It would be much easier if I could just store the data on the Raspi and pull it off when required but that’s not an option.

        If anyone knows of an easy way to send data from a Raspi to a website at given intervals, I’d love to hear from you.

  25. Krishna says:

    Hi!!

    Can someone please tell me how to adjust the sampling frequency? Say reducing to 20,000 samples/second?

    Thanks!

    • halherta says:

      Krishna the maximum sampling frequency for the MCP3008 is 200ksps at 5V and 75Ksps at 2.7V. At 3.3V I doubt that it would go above 100Ksps. Because the spidev interface is being used, there really is no way to directly control the sampling speed. You could speed up/slow down the speed of the spi clock (in the code example set to 1MHz…Maximum is 1.35MHz @ 2.7V) and call the spiWriteRead function at regular intervals using functions such as usleep or POSIX timers but I’m not sure what kind of sample rates you’ll get without doing some experiments. At maximum spi clock speed and calling spiWriteRead as fast as possible I doubt you’d be able to achieve anything near 20ksps. There will be also synchronicity issues…the delay between consecutive samples could exhibit a large variance. This solution is meant for applications where sampling speed requirements are very modest or even non-existent. If you have strict sampling requirements consider using a microcontroller with a decent ADC built-in and generous amounts of memory for buffering….and maybe a DMA unit e.g. STM32F, TIVA C , PIC32MX, PIC32MZ, e.t.c. The micro can then communicate with the RPI over UART, SPI or I2C.

  26. Krishna says:

    @Halherta: Thanks a lot for the info :)

    This might seem quite silly: How do I know what the current sampling frequency is?

    Thanks!

    • halherta says:

      Krishna, use the ADC to sample a sinewave with a known frequency. Then plot the samples. Another way is to toggle a GPIO after every call to spiWriteRead and view the generated squarewave on the scope (ofcourse toggling a gpio will itself take some time as well). Finally you could also use POSIX timers to time the spiWriteRead function call.

  27. Krishna says:

    @ Halherta

    Hi again! Need you help!

    1. I have copy-pasted the above code and ran the exact way as specified above. But I am getting an error. Please see this:

    https://drive.google.com/file/d/0B5nDZHlyAnjHUHdCMFI0cG9LTXM/edit?usp=sharing

    2. I also want to use ch1 along with ch0 i.e each time it enters the while loop in mcp3008SpiTest.cpp, I want the data from not just ch0 but also ch1. Could you please tell me what all changes to the code I’ll have to make? Would this be the only change?:

    data[1] = 0b10010000 |( ((a2dChannel & 7) << 4));

    Thank you very much!! :)

  28. Krishna says:

    @Halherta:

    1. The problem with the code went away by using this:

    tr -cd ‘\11\12\15\40-\176′ new.cpp
    g++ -Wall -o OutBin mcp3008Spi.cpp new.cpp
    sudo ./OutBin

    2. I could access the second channel using : data[1] = 0b10010000 |( ((a2dChannel & 7) << 4));

    - An interesting observation: When operating at 3.3V, so sampling frequency will be around 100,000 samples/sec. So if I put 'i' as 100,000 and measure the time the code takes to run. I had expected it to finish running in 1 second, but it took around 2.5 seconds. That means the code is a bit slow, for applications like time delay estimation. Would it possible to make the code run faster?

    Since raspberry pi is upto 700MHz, this might be possible? Thanks :)

    • halherta says:

      Krishna, I have the code in a GitHub repo github.com/halherta. You can git clone it to avoid this error caused by copying and pasting. Remember that while the RPi might be running at 700MHz..its running Linux which slows things down. You probably won’t get it to run any faster without developing a special optimized linux kernel driver/module for the MCP3008.

  29. Steve Ferry says:

    Hi,

    Further to my earlier post about gearbox monitoring in Norway, I have finished my design on an 8 channel ADC board and have placed an order for the PCB’s to be manufactured.

    The board complies with Halherta’s design above and will work with Hussan’s (git) code.

    The design features a nice stable 3.3V supply to the ADC chip smoothed and regulated from the Pi’s 5V supply. There are 5 X 3 pin sockets for 3.3V, 0V and GPIO pins
    1,4,5,6&7 and a 2 pin socket for a power on LED.

    The ADC is connected to CE0 or CE1 using a link so that with a daisychain ribbon cable, 2 boards can be used giving 16 single ended channels (or 8 differential channels).

    Each ADC input has a 3V3 zener diode overvoltage protection and a built in voltage divider so that 0-3.3V up to 0-20V can be applied. The board is connected to the Raspberry Pi using a 26 pin ribbon cable. Analog connections to the ADC are via on-board terminal strips.

    The boards are going to be available in self assembly kit form from mid May including everything except the ADC chip itself (because you can use MCP3008or MCP 3208 and possibly even MCP3304 although not tested with this board) for £12.50 + £2.50 mainland UK postage. Non UK mainland customers will be charged at packing cost plus royal mail delivery charges as per royal mail website.

    Please contact me at s.ferry@efftek-uk.com for further information.

    Thanks,

    Steve.

    • halherta says:

      Great job Steve! If anyone has a need for an ADC expansion board for the RPi, have a look at Steve’s board!

  30. Krishna says:

    Hi again!!

    Would it be possible to give negative inputs to the MCP3008? If yes, could you please tell me what all changes i’ll have to make?

    Thanks!

    • halherta says:

      Krishna, I doubt that the MCP3008 can handle negative singled-ended inputs…it can handle differential inputs however. Please check the datasheet

  31. Krishna says:

    Hi again!

    I just realized that the sampling rate is not uniform i.e the time interval between two samples isn’t constant. So that makes it impossible to work on speech related tasks as of now. Is there any way I can get a uniform sampling rate? It would really help me if I could get existing code, as I’m not good with coding.

    Many thanks!

    • halherta says:

      Krishna,
      Linux is not a real-time OS.. so no…getting a uniform sampling rate not easy and is probably impossible. Having said that you might get a slightly more uniform sampling rate if you bit-bang SPI via GPIOs…..http://www.raspberrypi.org/forums/viewtopic.php?p=536030#p536030

      For speech related tasks, I recommend using a USB microphone with PortAudio Audio I/O library. Its the same library that the Audacity software uses. Unfortunately I’ve never used PortAudio..but it seems to have some documentation and a programming tutorial on its site.

  32. Pingback: Add Analog to Digital Conversion Capability to the Raspberry Pi without using an ADC chip | Hertaville

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>