0

I am trying to get more than two chip selects available on the Pi. I have the devices present, but am struggling to read a MCP3008 when the CS is plugged into the additional GPIO pins. My end goal is to connect two MCP3008 and two MCP23S17 port expanders.

I have used the device tree overlay from here, copied below, to create an additional three chip select pins. This overlay results in chip select pins on GPIO 8, 7, 1, 5, and 6.

In /dev, I now have the following, as expected:

  • /dev/spidev0.0
  • /dev/spidev0.1
  • /dev/spidev0.2
  • /dev/spidev0.3
  • /dev/spidev0.4

Using the following sample code, very lightly modified from here - just to add the channel as a const and pass it into both wiringPiSPISetup() and wiringPiSPIDataRW() - I can read from standard GPIO 8 and 7, but not the other three. For those, I am just getting 0 returned from my MCP3008 input.

I have tried writing a 0 to physical pin 28, which I understand to be GPIO 1, but still no joy.

For reference, here's the result of gpio readall:

 +-----+-----+---------+------+---+---Pi 4B--+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
 |   2 |   8 |   SDA.1 |  OUT | 1 |  3 || 4  |   |      | 5v      |     |     |
 |   3 |   9 |   SCL.1 |  OUT | 1 |  5 || 6  |   |      | 0v      |     |     |
 |   4 |   7 | GPIO. 7 |  OUT | 1 |  7 || 8  | 1 | IN   | TxD     | 15  | 14  |
 |     |     |      0v |      |   |  9 || 10 | 1 | IN   | RxD     | 16  | 15  |
 |  17 |   0 | GPIO. 0 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |
 |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |
 |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |
 |  10 |  12 |    MOSI | ALT0 | 0 | 19 || 20 |   |      | 0v      |     |     |
 |   9 |  13 |    MISO | ALT0 | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |
 |  11 |  14 |    SCLK | ALT0 | 0 | 23 || 24 | 1 | OUT  | CE0     | 10  | 8   |
 |     |     |      0v |      |   | 25 || 26 | 1 | OUT  | CE1     | 11  | 7   |
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | OUT  | SCL.0   | 31  | 1   |
 |   5 |  21 | GPIO.21 |  OUT | 0 | 29 || 30 |   |      | 0v      |     |     |
 |   6 |  22 | GPIO.22 |  OUT | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |
 |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |
 |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |
 |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |
 |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+---Pi 4B--+---+------+---------+-----+-----+

and the c I'm using to test:


#include <wiringPi.h>
#include <wiringPiSPI.h>
#include <stdio.h>  
#include <stdlib.h>  
#include <stdint.h>
#include <string.h>
#include <time.h>
 
void mcp3008_read(uint8_t);

// channels 0 and 1 work, 2 - 4 don't
const int channel = 0; 
 
int main(void)
{
    wiringPiSetupGpio();
    wiringPiSPISetup(channel, 4*1000*1000); 
    delay(50);
    for (;;){
        mcp3008_read(0);
        delay(50);
    }
    
    return 0;
}

// read a channel
void mcp3008_read(uint8_t adcnum)
{ 
    unsigned int commandout = 0;
    unsigned int adcout = 0;

    commandout = adcnum & 0x3;  // only 0-7
    commandout |= 0x18;     // start bit + single-ended bit

    uint8_t spibuf[3];

    spibuf[0] = commandout;
    spibuf[1] = 0;
    spibuf[2] = 0;

    wiringPiSPIDataRW(channel, spibuf, 3);    

    adcout = ((spibuf[1] << 8) | (spibuf[2])) >> 4;

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

And finally, the device tree overlay:

/dts-v1/;
/plugin/;


/ {
    compatible = "brcm,bcm2835";

    fragment@0 {
        target = <&spi0_cs_pins>;
        frag0: __overlay__ {
            brcm,pins = <8 7 1 5 6>;
        };
    };

    fragment@1 {
        target = <&spi0>;
        frag1: __overlay__ {
            #address-cells = <1>;
            #size-cells = <0>;

            cs-gpios = <&gpio 8 1>, <&gpio 7 1>, <&gpio 1 1>, <&gpio 5 1>, <&gpio 6 1>;
            status = "okay";

            spidev0_2: spidev@2 {
                compatible = "spidev";
                reg = <2>;
                #address-cells = <1>;
                #size-cells = <0>;
                spi-max-frequency = <125000000>;
            };

            spidev0_3: spidev@3 {
                compatible = "spidev";
                reg = <3>;
                #address-cells = <1>;
                #size-cells = <0>;
                spi-max-frequency = <125000000>;
            };

            spidev0_4: spidev@4 {
                compatible = "spidev";
                reg = <4>;
                #address-cells = <1>;
                #size-cells = <0>;
                spi-max-frequency = <125000000>;
            };
        };
    };
};

I'm pretty new to GPIO, SPI and WiringPi, so I am assuming I may have a fundamental disconnect in my understanding.

4
  • 1. It is unclear what you are asking, BUT WiringPi is deprecated, so is a poor choice for new code (although it still works, but PI4 support is incomplete).
    – Milliways
    Sep 3 at 11:18
  • 1
    2. The gpio tool DOES NOT report ACTUAL pin configuration, but a fixed default pin function. See raspberrypi.stackexchange.com/a/123749/8697 for a tool which reports actual configured function.
    – Milliways
    Sep 3 at 11:21
  • 1
    3. AFAIK the wiringpi code uses its own direct register access to provide SPI, it does not use device tree settings. Indeed the device tree setting DOES NOT use the SOC CS code, but its own kernel driver, which is invisible to any gpio function tools.
    – Milliways
    Sep 3 at 11:25
  • 1
    wiringPi does use the Linux SPI driver (pigpio and bcm2835 use their own drivers as they provided SPI support before the Linux SPI driver was available on the Pi). The problem with wiringPi is it is likely to hard code support for two devices. You would need to modify the source and recompile.
    – joan
    Sep 3 at 13:02
1

If you want to use the Device Tree configuration options you MUST use the kernel SPI driver, not WiringPi.

Incidentally pins 27,28 are RESERVED and used for HAT (and probably other undocumented system functions).

5
  • Thank you so much. I have a lot to get up to speed with, all at once. I was using WiringPi largely because my ultimate goal is integration with PureData, and there are plugins available for WiringPi. However, I can of course just write my own. I'll check out your linked post and read up on the kernel SPI driver. Sep 3 at 11:39
  • 1
    @nullPainter I suggest you look at pigpio, which is extremely capable, but uses a socket (or pipe) interface which can take a bit of getting used to, or lgpio
    – Milliways
    Sep 3 at 11:48
  • I removed the overlay and gave the pigpio rawMCP3008.c sample a go, and it works wonderfully. Thanks heaps, Milliways and @joan. It's funny how following particular Raspberry Pi forum threads can lead one down the completely wrong path! Sep 4 at 0:58
  • Not entirely sure how to use that sample to read anything other than the first pin of the MCP3008, but I'm sure I'll work it out! Sep 4 at 1:19
  • Success! Ignored the raw approach and looked through the c reference documentation. Nice and elegant. Sep 4 at 9:03

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.