MCP4151 Digital pot on SPI using SPIDEV

Hi Guys,

I am having an issue with a simple circuit I have constructed using MCP4151 Digital potentiometer and 4 LEDs. Just to provide a bit of background: I am using 4 LEDs in a constellation as a camera target for distance calibration (for Intel RealSense depth cameras).

I am by no means an electronics expert, I’ve been a software engineer for all of my 15 years career and only just recently started to foray into electronics. Addresses, interfaces, timings, signals etc while I grasp basics I still need lots of practice to understand them.

I am hoping that the issue I am having right now will be easy to solve for someone with more experience than me.

The problem

I have constructed schematic from this example: MCP4151 on Raspberry Pi and tested it successfully on RasPi.
Since the system I am helping to develop will be entirely based on nVida Jeston Xavier NX platform, I moved my development to it. I have plugged the relevant cables to the SPI0_ interface and did the following:

  1. Using sudo /opt/nvidia/jetson-io/jetson-io.py I’ve activated the spi1(19,21,23,24,26) interface.
  2. I have then installed the spidev library through pip3

Then I have attempted to run the example code:

i#!/usr/bin/python
import spidev
import time

spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 976000

def write_pot(input):
msb = input >> 8
lsb = input & 0xFF
spi.xfer([msb, lsb])

while True:
for i in range(0x00, 0x1FF, 1):
    write_pot(i)
    time.sleep(.005)
for i in range(0x1FF, 0x00, -1):
    write_pot(i)
    time.sleep(.005)

Code executes without any errors but the potentiometer is not working at all, I checked the output pins with simple probe and the voltage stays 1.2v which makes me assume no signal is sent to the pot? The LEDs do not light up at all.

I scoured the forums and articles to find that people are having issues with SPI interface on Jetson Nano / TX2 or indeed NX (like I am using)

I have found several posts on these forums too, with complex solutions that I am frankly confused about.

Would anyone be willing to point me in the right direction? What am I doing wrong, where is the issue or what should I do in the configuration (if there is something else I need to do there)?

Is there more that needs to be done to enable SPI and SPIDEV on Jetson XAVIER Nx?

Cheers!

Could you try loopback test to verify the pin configure.

Hi Shane,

Thank you for your reply - this is a new concept for me.
I will read up on loopback tests (unless you have a guide / tutorial you could share which would be greatly appreciated) and will report back once I have some results.

Pawel

Hi Shane,

I have used a code originally written for RasPi for loobpack test:

/*
 * SPI testing utility (using spidev driver)
 *
 * Copyright (c) 2007  MontaVista Software, Inc.
 * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
 */

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

static void pabort(const char *s)
{
	perror(s);
	abort();
}

static const char *device = "/dev/spidev1.1";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;

static void transfer(int fd)
{
	int ret;
	uint8_t tx[] = {
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
		0xF0, 0x0D,
	};
	uint8_t rx[ARRAY_SIZE(tx)] = {0, };
	struct spi_ioc_transfer tr = {
		.tx_buf = (unsigned long)tx,
		.rx_buf = (unsigned long)rx,
		.len = ARRAY_SIZE(tx),
		.delay_usecs = delay,
		.speed_hz = speed,
		.bits_per_word = bits,
	};

	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
	if (ret < 1)
		pabort("can't send spi message");

	for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
		if (!(ret % 6))
			puts("");
		printf("%.2X ", rx[ret]);
	}
	puts("");
}

static void print_usage(const char *prog)
{
	printf("Usage: %s [-DsbdlHOLC3]\n", prog);
	puts("  -D --device   device to use (default /dev/spidev1.1)\n"
	     "  -s --speed    max speed (Hz)\n"
	     "  -d --delay    delay (usec)\n"
	     "  -b --bpw      bits per word \n"
	     "  -l --loop     loopback\n"
	     "  -H --cpha     clock phase\n"
	     "  -O --cpol     clock polarity\n"
	     "  -L --lsb      least significant bit first\n"
	     "  -C --cs-high  chip select active high\n"
	     "  -3 --3wire    SI/SO signals shared\n");
	exit(1);
}

    static void parse_opts(int argc, char *argv[])
    {
    	while (1) {
    		static const struct option lopts[] = {
    			{ "device",  1, 0, 'D' },
    			{ "speed",   1, 0, 's' },
    			{ "delay",   1, 0, 'd' },
    			{ "bpw",     1, 0, 'b' },
    			{ "loop",    0, 0, 'l' },
    			{ "cpha",    0, 0, 'H' },
    			{ "cpol",    0, 0, 'O' },
    			{ "lsb",     0, 0, 'L' },
    			{ "cs-high", 0, 0, 'C' },
    			{ "3wire",   0, 0, '3' },
    			{ "no-cs",   0, 0, 'N' },
    			{ "ready",   0, 0, 'R' },
    			{ NULL, 0, 0, 0 },
    		};
    		int c;

    		c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);

    		if (c == -1)
    			break;

    		switch (c) {
    		case 'D':
    			device = optarg;
    			break;
    		case 's':
    			speed = atoi(optarg);
    			break;
    		case 'd':
    			delay = atoi(optarg);
    			break;
    		case 'b':
    			bits = atoi(optarg);
    			break;
    		case 'l':
    			mode |= SPI_LOOP;
    			break;
    		case 'H':
    			mode |= SPI_CPHA;
    			break;
    		case 'O':
    			mode |= SPI_CPOL;
    			break;
    		case 'L':
    			mode |= SPI_LSB_FIRST;
    			break;
    		case 'C':
    			mode |= SPI_CS_HIGH;
    			break;
    		case '3':
    			mode |= SPI_3WIRE;
    			break;
    		case 'N':
    			mode |= SPI_NO_CS;
    			break;
    		case 'R':
    			mode |= SPI_READY;
    			break;
    		default:
    			print_usage(argv[0]);
    			break;
    		}
    	}
    }

    int main(int argc, char *argv[])
    {
    	int ret = 0;
    	int fd;

    	parse_opts(argc, argv);

    	fd = open(device, O_RDWR);
    	if (fd < 0)
    		pabort("can't open device");

    	/*
    	 * spi mode
    	 */
    	ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
    	if (ret == -1)
    		pabort("can't set spi mode");

    	ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
    	if (ret == -1)
    		pabort("can't get spi mode");

    	/*
    	 * bits per word
    	 */
    	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    	if (ret == -1)
    		pabort("can't set bits per word");

    	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
    	if (ret == -1)
    		pabort("can't get bits per word");

    	/*
    	 * max speed hz
    	 */
    	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    	if (ret == -1)
    		pabort("can't set max speed hz");

    	ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
    	if (ret == -1)
    		pabort("can't get max speed hz");

    	printf("spi mode: %d\n", mode);
    	printf("bits per word: %d\n", bits);
    	printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);

    	transfer(fd);

    	close(fd);

    	return ret;
    }

Source: RASPBERRY PI SPI LOOPBACK TESTING

From my understanding of the code this should work fine on Xavier NX or indeed on any dev board with spi interface.

I have compiled the code as per the instructions on the website:

$ gcc -o spidev_test spidev_test.c

Then I have executed the code using the same instruction as in the tutorial:

$ ./spidev_test -D /dev/spidev0.0

And the results are:

spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)

FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF

Which from the documentation of this little useful program would mean that the SPI interface is indeed working correctly? Unless (and this is where my knowledge is still lacking) getting FF or 00 throughout means that it is not.

I have tested this on RasPi just to make sure it works, and the result is as bove but with 00 not FF.

EDIT:
Update, I had the wrong pins shorted :facepalm: , now my results are:

spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)

FF FF FF FF FF FF
40 00 00 00 00 95
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
DE AD BE EF BA AD
F0 0D

Which confirms that the SPI is indeed working.

If the loopback test is working that tell the pinmux configure without problem.
Maybe try change the speed for the device to check.

1 Like

Hi Shane,

I can confirm that SPI interface works now. Turns out there is a problem with my circuit and that Xavier NX does indeed output correct signal to the SPI interface. I have tested the pot with a simple resistance meter and could see the resistance going up and down. My circuit does not work still but that has nothing to do with Xavier and SPI.

Thank you for taking time to help me out with this.

Pawel