SPI and Apalis imx6

Hello,

I am working with the Eagle board from Diamond System that has Apalis imx6 on it and I would like to communicate with SPI component LTC1858 datasheet using /dev/spidev1.0. The TX nb of bits is 8 and the RX nb of bits is 14.

It seems like I can kind of get data back from the component (I checked with an oscilloscope, what I see is what I get, I can’t check again until Friday), it’s just that the values sent back are not what I am expecting. The values I get are about 0.82*what I expect (ex, 10V input brings 8.2V spi data). I’m wondering if maybe the configuration is sent wrong… Not sure how to deal with the TX 8 bit and RX 14 bit business… I haven’t done anything with CS. Is there something wrong with the code I have been using? Is there a library I could use instead?

If I put the code in 16 bit version I get similar results.

Board info
uname -a info: Linux apalis-imx6 3.14.52-00009-g786c368 #20 SMP Sat Jun 17 11:27:18 IST 2017 armv7l GNU/Linux. Hardware: The board I’m working with is EGL-MX6-Q2G-XT from Diamond System. It’s an ARM SBC using Toradex Apalis iMX6. The OS is Angstrom.

Best regards,

Jennifer

8 bit version

    /*
     * 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.0";
    static uint8_t mode=0;
    static uint8_t bits = 8;
    static uint32_t speed = 1000;
    static uint16_t delay  ;
    
    static void printHexToBinary(uint16_t hex)
    {
      switch (hex)
      {
        case 0: printf("0000");
    	      break;
        case 1: printf("0001");
    	      break;
        case 2: printf("0010");
    	      break;
        case 3: printf("0011");
    	      break;
        case 4: printf("0100");
    	      break;
        case 5: printf("0101");
    	      break;
        case 6: printf("0110");
    	      break;
        case 7: printf("0111");
    	      break;
        case 8: printf("1000");
    	      break;
        case 9: printf("1001");
    	      break;
        case 10: printf("1010");
    	      break;
        case 11: printf("1011");
    	      break;
        case 12: printf("1100");
    	      break;
        case 13: printf("1101");
    	      break;
        case 14: printf("1110");
    	      break;
        case 15: printf("1111");
    	      break;
        default: break;
      }
    }
    
    static void transfer(int fd)
    {
    	int ret;
            uint8_t tx[] = {
                  (  0xAC )
    	};
            uint8_t rx[2] = {0, };
    	struct spi_ioc_transfer tr = {
    		.tx_buf = (unsigned long)tx,
    		.rx_buf = (unsigned long)rx,
             	.len = ARRAY_SIZE(rx),
    		.delay_usecs = delay,
    		.speed_hz = speed,
    		.bits_per_word = 8,
    	};
    	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    	if (ret < 1)
    		pabort("can't send spi message");
    	for (ret = 0; ret < ARRAY_SIZE(rx); ret++) {
    	//	if (!(ret % 6))
    	//		puts("");
    		printf("%.2X ", rx[ret]);
    	}
    	printf("\r\n");
            uint16_t total = (rx[0] << 8) + rx[1];
    	printf("%d\r\n", total);
    
            printHexToBinary(rx[0]>>4);
    	printHexToBinary(rx[0] & 0x0f);
    	printHexToBinary((rx[1]>>4) & 0x0f);
    	printHexToBinary(rx[1] & 0x0f);
            printf("\r\nvoltage: %f", (double)total*10/(4*16383));
    	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;
    }

16 bit version

/*
 * 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.0";
static uint8_t mode=0;
static uint8_t bits = 16;
static uint32_t speed = 100000;
static uint16_t delay  ;

static void printHexToBinary(uint16_t hex)
{
  switch (hex)
  {
    case 0: printf("0000");
	      break;
    case 1: printf("0001");
	      break;
    case 2: printf("0010");
	      break;
    case 3: printf("0011");
	      break;
    case 4: printf("0100");
	      break;
    case 5: printf("0101");
	      break;
    case 6: printf("0110");
	      break;
    case 7: printf("0111");
	      break;
    case 8: printf("1000");
	      break;
    case 9: printf("1001");
	      break;
    case 10: printf("1010");
	      break;
    case 11: printf("1011");
	      break;
    case 12: printf("1100");
	      break;
    case 13: printf("1101");
	      break;
    case 14: printf("1110");
	      break;
    case 15: printf("1111");
	      break;
    default: break;
  }
}

static void transfer(int fd)
{
	int ret;
        uint16_t tx[] = {
              (  0x2C00 )
	};
        uint16_t rx[2] = {0, };
//	uint16_t rx[1] ;
	struct spi_ioc_transfer tr = {
		.tx_buf = (unsigned long)tx,
		.rx_buf = (unsigned long)rx,
         	.len = 2,
		.delay_usecs =4,
		.speed_hz = speed,
		.bits_per_word = 16,
//		.tx_nbits = 8,
//		.rx_nbits = 16,
//		.pad = 0
	};
	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
	if (ret < 1)
		pabort("can't send spi message");
	for (ret = 0; ret < ARRAY_SIZE(rx); ret++) {
	//	if (!(ret % 6))
	//		puts("");
		printf("%.2X ", rx[ret]);
	}
	printf("\r\n");
	if (bits == 1)
	{
          int temp = rx[0];
	  rx[0] = rx[1];
	  rx[1] = temp;
	}
//        uint16_t total = (rx[0] << 8) + rx[1];
	uint16_t total = rx[0];
	printf("%d\r\n", total);

        printHexToBinary(rx[0]>>12);
	printHexToBinary(rx[0] >> 8);
	printHexToBinary((rx[0]>>4) & 0x0f);
	printHexToBinary(rx[0] & 0x0f);
        printf("\r\nvoltage: %f", (double)total*10/(4*16383));
        printf("\r\nvoltage/0.82: %f", (double)total*10/(4*16383*0.82));
	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;
}

Hi @JG2

Thanks for writing to the Toradex Community!

This factor 0.82 is strange. As you said, could you check if the data sent on SPI is what you really get in the Software.

printf("\r\nvoltage: %f", (double)total10/(416383));

Why 16383, should not this be 16384?

Regarding the factor, for the SPI chip LTC1858 you can use an internal Reference or an external Reference. Which one are you using to measure the voltage?

Best regards,
Jaski

Hi Jaski,

Thanks for your answer. I can only go to the lab with the oscilloscope on Fridays so I have to wait until tomorrow to check on the oscilloscope again. But last week the data sent on SPI was what I was getting in the software.

I used 16383 because the max value is 2^14 - 1, 14 bit ADC. Is that the correct way to do it?

I am wondering if I have made a mistake with the wiring. I have been using the CS from the Eagle/apalis board for the CONVST of the component. But I just measured with a multimeter and it looks like CONVST is high all the time. Maybe I’ll try using a GPIO instead and see what it does. I’m running out of ideas.

I am using the internal reference. I have not plugged any components or inputs to the pins VREF and REFCOMP.

Best regards,

Jennifer

Well, just did the test with a custom CONVST using a GPIO instead of CS, seems to work!!!

Thanks again for your help!

Thanks for the feedback. You are welcome.

Usually you should define the CS as GPIO and not Hardware CS. Due to the Spi’s driver Implementation of NXP, when muxing chip select to hardware native chipselect, then the CS is pulled high after every 8 bit of transfer which will lead to getting wrong data as in your case.

Best regards,
Jaski

Thank you for the explanation!

Best regards,

Jennifer

You are welcome.