Rpy
Posts: 4
Joined: Sun Aug 25, 2019 3:40 am

Reading different channels from MCP3008 with pigpio

Sun Aug 25, 2019 5:33 pm

Hi,
I have been working on a project for which I need an ADC: the MCP3008. This chip has 8 different channels, uses a Serial Peripheral Interface (SPI) and has a 10 bit resolution.
Using the example C code from the pigpio library for bit-banging the ADC, I have managed to take in readings from channel 0. However, if I change the channel input, the readings become 0.

Code: Select all

/*
   MCP3004/8 10-bit ADC 4/8 channels

   1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17
   SB SD D2 D1 D0 NA NA B9 B8 B7 B6 B5 B4 B3 B2 B1 B0

   SB       1
   SD       0=differential 1=single
   D2/D1/D0 0-7 channel
   */
   for (i=0; i<BUFFER; i++){
      buf[0] = 0xC0; // Start bit, single ended, channel 0.
      rawWaveAddSPI(&rawSPI, offset, SPI_SS, buf, 2, BX, B0, B0);
      offset += REPEAT_MICROS;
   }
Of course I need to give five control bits to the ADC before the conversion starts, one start bit, one for the single or differential options, the rest are for the channel selection. The example code seems to indicate that these control bits are contained in hexadecimal C0 (11000000 in binary). The first five bits correspond to: START BIT, SINGLE ENDED, CHANNEL 0. Just like the comment said. I have tried changing the hex number to C8 to get 11001000 in binary: START BIT, SINGLE ENDED, CHANNEL 1. The program did not function properly.

I have been spending lots of time trying to decode the program but I'm still stuck. Could someone please help me with this issue? How do I send the right bits to the chip so I can get the right readings?

Full example code: http://abyz.me.uk/rpi/pigpio/examples.html#C%20code (SPI bit bang MCP3008)
ADC documentation: https://cdn-shop.adafruit.com/datasheets/MCP3008.pdf

P.S.: I'm just a kid. I've learned C about a month ago. Dealing with advanced and expert code is a great challenge for me.

User avatar
joan
Posts: 14697
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Reading different channels from MCP3008 with pigpio

Sun Aug 25, 2019 9:00 pm

I am not surprised you are having problems. It's very good of you to have made any progress at all using that code. The linked code is highly specialised for a unique purpose (reading multiple ADC chips simultaneously).

Forget that code and find something simpler.

C

Code: Select all

#include <stdio.h>
#include <stdlib.h>

#include <pigpio.h>

/*
   gcc -pthread -o mcp3008 mcp3008.c -lpigpio
*/

int main(int argc, char *argv[])
{
   int i;
   int h;
   int v;
   int loops;
   int speed;
   double start, diff, sps;
   unsigned char buf[3];

   if (argc > 1) loops = atoi(argv[1]);
   else loops = 1000000;

   if (argc > 2) speed = atoi(argv[2]);
   else speed = 1000000;

   if (gpioInitialise() < 0) return 1;

   h = spiOpen(0, speed, 0);

   if (h < 0) return 2;

   start = time_time();

   for (i=0; i<loops; i++)
   {
      buf[0] = 1;
      buf[1] = 128;
      buf[2] = 0;

      spiXfer(h, buf, buf, 3);

      v = ((buf[1]&3)<<8) | buf[2];

      printf("%d\n", v);
   }

   diff = time_time() - start;

   fprintf(stderr, "sps=%.1f @ %d bps (%d/%.1f)\n",
      (double)loops / diff, speed, loops, diff);

   spiClose(h);

   gpioTerminate();

   return 0;
}
Python

Code: Select all

#!/usr/bin/env python

import pigpio

def read_MCP3008(adc, channel):
   count, data = pi.spi_xfer(adc, [1, (8+channel)<<4, 0])
   value = ((data[1] << 8) | data[2]) & 0x3FF
   return value

pi = pigpio.pi()

if not pi.connected:
   exit()

adc1 = pi.spi_open(0, 50000, 0) # CE0
adc2 = pi.spi_open(1, 50000, 0) # CE1

for c in range(8):
   print(read_MCP3008(adc1, c), read_MCP3008(adc2, c))

pi.spi_close(adc1)
pi.spi_close(adc2)

pi.stop()

User avatar
DougieLawson
Posts: 37134
Joined: Sun Jun 16, 2013 11:19 pm
Location: Basingstoke, UK
Contact: Website Twitter

Re: Reading different channels from MCP3008 with pigpio

Sun Aug 25, 2019 9:12 pm

There's some C++ code at: https://github.com/halherta/RaspberryPi-mcp3008Spi which uses spidev to read channel 0 of an MCP3008 on /dev/spidev0.0. You should be able to hack that to read channels 0-7.

There's a plain C example at: https://gist.github.com/dansimpson/6259253

That's if you don't want to use Joan's examples.
Note: Having anything humorous in your signature is completely banned on this forum. Wear a tin-foil hat and you'll get a ban.

Any DMs sent on Twitter will be answered next month.

This is a doctor free zone.

User avatar
RogerW
Posts: 286
Joined: Sat Dec 20, 2014 12:15 pm
Location: London UK

Re: Reading different channels from MCP3008 with pigpio

Mon Aug 26, 2019 10:35 am

I have some C++ code which (among other things) reads data from a MCP3008. It uses the pigpio c library not the daemon.
Header file

Code: Select all

[// Rpio.h
// Written by Roger Woollett
//
// Low level interface to hsrdware via gpio
// Uses the pigpio library and the base class
// PioBase ensures that the library is initialised only once.

#ifndef RPIO_H
#define RPIO_H

#include <pigpio.h>
#include <stdexcept>

class PioBase
{
public:
    PioBase()
    {
        if (!m_count++)
            gpioInitialise();
	}
    ~PioBase()
    {
        if(!--m_count)
            gpioTerminate();
    }

private:
    static int m_count;
};

class LED:private PioBase
// A simple class for an LED
{
public:
	LED(int pin);
	~LED();

	// no default or copy constructorsself,pi,
	LED() = delete;
	LED(const LED&) = delete;

	void On()
	{
		gpioWrite(m_pin,1);
	}
	void Off()
	{
		gpioWrite(m_pin,0);
	}

private:
    int m_pin;
};

class ADC: private PioBase
{
/*  class to access a MCP3008 A to D converter
    pins used are:
    Name board(gpio) - chip pin
    MOSI    19(GPIO10)    11 - data in
    MISO    21(GPIO9)     12 - data out
    SCLK    23(GPIO11)    13 - clock
    CE0    24(GPIO8)      10 - chip select
    CE1    26(GPIO7)      10 - chip select on second chip
    There is a second set of pins used on A+ B+ and Pi2
    MOSI  38(GPIO20)      11 - data in
    NISO  35(GPIO19)      12 - data out
    SCLK  40(GPIO21)      13 - clock
    CE0   12(GPIO18)      10 - chip select
    CE1   11(GPIO17)      10 - chip select
    CE2   36(GPIO16)      10 - chip select
    power connections
           Ground        9mc    DGND
            Ground        14    AGND

    SCLK    23(GPIO11)    13 - clock
    CE0    24(GPIO8)      10 - chip select
    CE1    26(GPIO7)      10 - chip select on second chip
    There is a second set of pins used on A+ B+ and Pi2
    MOSI  38(GPIO20)      11 - data in
    NISO  35(GPIO19)      12 - data out
    SCLK  40(GPIO21)      13 - clock
    CE0   12(GPIO18)      10 - chip select
    CE1   11(GPIO17)      10 - chip select
    CE2   36(GPIO16)      10 - chip select
    power connections
           Ground        9mc    DGND
            Ground        14    AGND
           3.3V           15    VREF
           3.3V           16    VDD
  */
public:
	ADC(unsigned ce = 0,bool aux = false);
	~ADC();
	ADC(const ADC&) = delete;

	unsigned Read(unsigned channel);
private:
	int m_handle;
};

class Motor: private PioBase
{
    // Motor controls two pins which are connected to
    // one half of a dual H-Bridge controller

    // I use a SN74110NE
public:
    Motor(int pin1,int pin2,int frequency = 1500);
        // For forward pwm to pin 1, 0 to pin2
        // for reverse pwm to pin 2, 0 to pin 1
        // speed input is -100 - 0 - 100
        // When a change of direction is requested
        // the motor is stopped but only briefly.
        // Caller should arrange a longer spin down/up
        // time if required
    Motor() = delete;
    Motor(const Motor&) = delete;

    void Go(int speed);
    void Stop();

private:
    int m_pin1;
    int m_pin2;

    bool m_forward;
};

#endif // RPIO_H
Code file

Code: Select all

// Rpio.cpp
// Written by Roger Woollett

#include "Rpio.h"

int PioBase::m_count = 0;

ADC::ADC(unsigned ce,bool aux)
{
	// standard port has two chip select, second port has three
	unsigned flags;
	if (aux)
	{
		flags = 0x100;    // set A bit
            if(ce < 0 or ce > 2)
				throw std::invalid_argument("ce must be 0 (CE0), 1 (CE1) or 2 (CE2)");
	}
	else
	{
		flags = 0;
		if(ce < 0 or ce > 1)
			throw std::invalid_argument("ce must be 0 (CE0) or 1 (CE1)");
	}

    m_handle = spiOpen(ce,100000,flags);
}
ADC::~ADC()
{
	spiClose(m_handle);
}
unsigned ADC::Read(unsigned channel)
{
/*	channel corresponds to pins 1 (ch0) to 8 (ch7)
    we have to send three bytes
    byte 0 has 7 zeros and a start bit
    byte 1 has the top bit set to indicate
    single rather than differential operation
    the next three bits contain the channel
    the bottom four bits are zero
    byte 2 contains zeros (don't care)
    3 bytes are returned
    byte 0 is ignored
    byte 1 contains the high 2 bits
    byte 2 contains the low 8 bits
*/
    if(channel < 0 or channel > 7)
		throw std::invalid_argument("channel must be 0 - 7");


	char send[3];
	send[0] = 1;
	send[1] = (0b1000 | channel) << 4;
	send[2] = 0;

	char rcv[4];

	int count = spiXfer(m_handle,send,rcv,3);

	// return value 0 - 1023
	if (count > 0)
		return (rcv[1] << 8) | rcv[2];
	else
	    return 0;
}

Motor::Motor(int pin1,int pin2,int frequency)
{
    m_pin1 = pin1;
    m_pin2 = pin2;

	gpioSetMode(pin1,PI_OUTPUT);
	gpioSetPWMrange(pin1,100);
	gpioSetPWMfrequency(pin1,frequency);

	gpioSetMode(pin2,PI_OUTPUT);
	gpioSetPWMrange(pin2,100);
	gpioSetPWMfrequency(pin2,frequency);

	m_forward = true;
}

void Motor::Go(int speed)
{

    bool forward = speed >= 0;
    if(forward != m_forward)
    {
        Stop();
        m_forward = forward;
    }

    if(forward)
    {
        gpioPWM(m_pin1,speed);
        gpioPWM(m_pin2,0);
    }
    else
    {
        gpioPWM(m_pin1,0);
        gpioPWM(m_pin2,-speed);
    }
}

void Motor::Stop()
{
    gpioPWM(m_pin1,0);
    gpioPWM(m_pin2,0);
}

Return to “C/C++”