RaspberryPi2, SPI, MCP3008

edited March 2016 in Raspberry PI

Hi,

I am working on a Raspberry PI project that should include a sharp distance sensor. As this sensor is analog I have to convert the analog data to digital with a converter IC via SPI. I have to be honest! How to communicate with this kind of chip is rocket science to me as it includes bit shifting, etc. I tried to be "smart" and use the SPI sketch that comes with the I/O examples, but this one uses the MCP3001. As expected that did not work. On the web I found several projects using the MCP3008 but with python.Unfortunately I cannot manage to translate that to processing. Can anybody help or has even already written something for the MCP3008!? Here you can find the python script for the chip: https://learn.adafruit.com/reading-a-analog-in-and-controlling-audio-volume-with-the-raspberry-pi/script

Would be great if anybody could help! Thank You!

Tagged:

Answers

  • edited April 2016

    This works for me. Make sure SPI is enabled in raspi-config. https://github.com/fuzzySi/pi_processing/blob/master/MCP3008_SPI_test.pde

    import processing.io.*;
    SPI MCP;
    int CHANNELS = 8; // 8 for MCP 3008, 4 for MCP3004
    int[] channel = new int[CHANNELS];
    
    void setup() {
      // printArray(SPI.list()); // to check SPI enabled - should give [0] "spidev0.0", [1] "spidev0.1"
      MCP = new SPI(SPI.list()[0]); // raspberry pi has 2 SPI interfaces, SPI.list()[1] is the other
      MCP.settings(1000000, SPI.MSBFIRST, SPI.MODE0); // 1MHz should be OK...
    }
    
    void draw() {
      for (int i = 0; i < CHANNELS; i ++) {
        channel[i] = returnADC(i);
        println("result for channel " + i + " is " + channel[i]);
      }
      delay(500);
    }
    
    int returnADC(int ch) {
      byte[] out = {1, byte((8 + ch) << 4), 0}; // outgoing bytes to the MCP
      byte[] in = MCP.transfer(out); // sends & returns
      int highInt = in[1] & 0xFF; // processing sees bytes as signed (-128 to 127) which is not what we want here
      int lowInt = in[2] & 0xFF; // doing this converts to unsigned ints (0 to 255)
      int result = ((highInt & 3) << 8) + lowInt; // adds lowest 2 bits of 2nd byte to 3rd byte to get 10 bit result 
      return result;
    }
    
  • edited May 2016

    There is an error in the example code when you run it on the Raspberry Pi 3. It is to do with grabbing the data as it comes back from the SPI port. This is what the MCP 300x class should be:-

    // MCP3004 is a Analog-to-Digital converter using SPI
    // this has 4 input channels
    // datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf
    import processing.io.SPI;
    class MCP3004 extends SPI {
    
      MCP3004(String dev) {
        super(dev);
        super.settings(500000, SPI.MSBFIRST, SPI.MODE0);
      }
    
      float getAnalog(int channel) {
        if (channel < 0 ||  channel > 3) {
          System.err.println("The channel needs to be from 0 to 3");
          throw new IllegalArgumentException("Unexpected channel");
        }
        byte[] out = { 0, 0, 0 };
        // encode the channel number in the first byte
        out[0] = (byte)(0x18 | channel);
        byte[] in = super.transfer(out);
        int val = ((in[1] & 0x3f)<< 4 ) | ((in[2] & 0xf0) >> 4);
        // val is between 0 and 1023
        return float(val)/1023.0;
      }
    }
    

    Note the code in the previous post will not work on the Pi 3.

  • Previous code does actually work for me on my pi3, on raspbian Jessie.

  • You sure of that? How are you testing it? I am using two thumb joysticks for positioning a circle on the screen. With the code you say works, while the circle movement tracks to some extent the joystick movements, it is very noisy and the circle makes several transitions across the screen for just a 90 degree joystick movement. I also have raspbian Jessie on my Pi. I just tested this on a Pi 2 and get the same results. Can you try my code and see if your results are more stable and consistent?

  • I've no doubt you're right - I'm sampling a control voltage and using it to set the pitch of a sample played with beads library, so I wouldn't really notice lack of accuracy. I'll have a play and look more closely.

  • edited February 2017

    sorry for digging out this old thread when using the code above i got the wrong data on a raspberry 3 (haven't tried it on a model 2 yet). the data i got was instead of one rise between 0 and 1 several circles. it almost looked like a sawtooth when turning my potentiometer.

    i figured out, that the problem was the following line: int val = ((in[1] & 0x3f)<< 4 ) | ((in[2] & 0xf0) >> 4);

    by removing ">> 4" from the second byte operation I got the correct data. does this make sense?

    so my calculation looked like this and yields the correct results: int val = ((in[1] & 0x3f)<< 4 ) | (in[2] & 0xf0);

    this was the first time i was working with bytes in processing so i am wondering whether this makes sense or not?

  • int val = ((in[1] & 0x3f)<< 4 ) | (in[2] & 0xf0);

    are these 8 bit values?

    that << 4 in the first bit and the mask in the second bit will give you something like xxxx0000 where the xs are a bitwise or of bottom half of the first with the top half of the second, which doesn't seem right.

  • (with full int values you'll get

    00000000 00000000 000000xx xxxx0000

    which is a multiple of 16 less than 1024)

Sign In or Register to comment.