I am reading an MCP3008 ADC using pigpio, I've abstracted the script below.

I have also found a slightly different construction or parsing of the three SPI command bytes and reconstruction of the ADC values in this Adafruit_MCP3008 script.

In my implementation (based on @joan's comments) I treat the three command bytes as they are shown in the MCP3008 chip documentation literally. First byte is only 0x01 for example. The Adafruit implementation stuffs all instruction bits into the first byte, leaving the 2nd and 3rd bytes as 0x00.

I think I can understand how that works, it's just a 1 bit frame change, and so the returned ADC value is strung out over all three returned bytes instead of only the last two for my implementation.

Is there likely to be any advantages or disadvantages to either implementation, or are they for all practical purposes identical and interchangeable (except perhaps for a small difference in speed due to python differences)?

enter image description here

For channels 4-7 here are the three bytes for each, the results are the same:

my implementation

['0b1', '0b11000000', '0b0']
['0b1', '0b11010000', '0b0']
['0b1', '0b11100000', '0b0']
['0b1', '0b11110000', '0b0']

[0, 339, 678, 1021]

implementation of Adafruit_MCP3008

['0b11100000', '0b0', '0b0']
['0b11101000', '0b0', '0b0']
['0b11110000', '0b0', '0b0']
['0b11111000', '0b0', '0b0']

[0, 339, 678, 1021]

my implementation

def _digitize_quick(self, channels=None):
    if channels == None:
        channels = range(8)

    values = []
    for i_channel in channels:

        three_bytes      = [1, (8+i_channel)<<4, 0]  
        print [bin(x) for x in three_bytes]

        cnt, rawdata     = self.pi.bb_spi_xfer(self.CSbar, three_bytes) # http://abyz.me.uk/rpi/pigpio/python.html

        adc_value        = ((rawdata[1]<<8) | rawdata[2]) # & 0x3FF left off for debugging


    return values

implementation of Adafruit_MCP3008

def _digitize_alt(self, channels=None):
    """from https://github.com/adafruit/Adafruit_Python_MCP3008/blob/master/Adafruit_MCP3008/MCP3008.py"""

    if channels == None:
        channels = range(8)

    results = []
    for i_channel in channels:

        command  = 0b11 << 6                # Start bit, single channel read
        command |= (i_channel & 0x07) << 3  # Channel number (in 3 bits)

        # Note the bottom 3 bits of command are 0, this is to account for the
        # extra clock to do the conversion, and the low null bit returned at
        # the start of the response.

        three_bytes = [command, 0x0, 0x0]
        print [bin(x) for x in three_bytes]

        cnt, resp = self.pi.bb_spi_xfer(self.CSbar, three_bytes)

        result    = (resp[0] & 0x01) << 9
        result   |= (resp[1] & 0xFF) << 1
        result   |= (resp[2] & 0x80) >> 7

        results.append(result & 0x3FF)

    return results
  • "I can't see any real difference" may turn out to be a reasonable answer in this case. It can always be revised later when something blows up ;-) – uhoh Jan 7 '19 at 5:55

The difference is whether you put the padding on the left or the right side of the data. The Adafruit implementation seems to start sending data immediately, which leaves the last 7 bits as padding, whereas keeping the first 7 bits as padding gives you the data in the last two bytes as in your implementation.

While the datasheet shows the padding before the data, it doesn't suggest there's anything wrong with either option (or any splits other than 0/7 or 7/0, padding differently could align the first bit of the response with the first bit of the second byte for instance).

If you had a SPI bus that could send 21-bit messages, you could skip the padding entirely and only exchange the required data and transact more sample readings per second (such a bus is required for full 200 ksps rate), but the RPi (and many other devices) are limited to multiples of 8-bit messages and padding is the easiest compromise.

I don't have one of the ADC chips to play with myself, but I've wondered if you could skip the padding (as per the Adafruit version) and only send/receive two bytes and get 9 bits of result back instead of the full 10.

| improve this answer | |
  • Hey thank you very much for the late answer! I will fire up my PI this evening and have a look. – uhoh May 14 '19 at 7:38
  • 1
    On the auxiliary SPI device you can set variable word sizes and 21 bits is permissible. pigpio allows this, see spi_open. I doubt it would make any measurable difference to the reads per second though. – joan May 14 '19 at 8:32
  • I'm only familiar with the linux kernel implementation that does have an 8 bit word limit kernel driver a bitbanging interface or userspace driver could in theory do whatever it wants, the 8-bit limit may only be for required for DMA transfers but enforced all the time to make the implementation simpler. – btharper May 17 '19 at 13:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.