SPI support

From www.chip-community.org
Jump to: navigation, search

This tutorial uses the rd235a kernel.

In fact spi is not currently configured in the standard device tree provided with NTC's standard kernel (March 12 2016).

To use SPI load the spidev module.

sudo modprobe spidev

When the module is loaded SPI2.0 is available as the device /dev/spi32766.0. (only one chip-select cs bit is available on U14).

The mapping is the following:

U14 pin 27 (tag CSIPCK) is SPI2-CS0
U14 pin 28 (tag CSICK) is SPI2-CLK
U14 pin 29 (tag CSIHSYNC) is SPI2-MOSI
U14 pin 30 (tag CSIVSYNC) is SPI2-MISO

The SPI bug can be tested using a test program like spidev_test.c

Download and compile this test program.

wget http://www.raspibo.org/renzo/spidev_test.c 
gcc -o spidev_test spidev_test.c

Now run it, this should be the output:

sudo ./spidev_test -D /dev/spidev32766.0 
spi device: /dev/spidev32766.0
spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)

00 00 00 00 00 00 
00 00 00 00 00 00 
00 00 00 00 00 00 
00 00 00 00 00 00 
00 00 00 00 00 00 
00 00 00 00 00 00 
00 00 

Now set up a jumper beween MISO and MOSI (U14 pins 29 and 30) and run the test again:

sudo ./spidev_test -D /dev/spidev32766.0 
spi device: /dev/spidev32766.0
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 

This shows that the SPI interface is reading its own output.

I have tested the SPI interface using two ADC ICs: MCP 3008 and MCP3204.

This is the schematics I used to connect a MCP3008:

          MCP3008       PIN CHIP
          +--v--+
  in-ch0--|1  16|---VDD (+3.3V)
  in-ch1--|2  15|--Vref (+3.3V or another reference)
  in-ch2--|3  14|--AGND (GND)
  in-ch3--|4  13|---CLK (SPI CLK <-> U14 pin 28 - CSICK)
  in-ch4--|5  12|--DOUT (SPI MISO <-> U14 pin 30 - CSIVSYNC)
  in-ch5--|6  11|---DIN (SPI MOSI <-> U14 pin 29 - CSIHSYNC)
  in-ch6--|7  10|----CS (SPI CS0 <-> U14 pin 27 - CSIPCK)
  in-ch7--|8   9|--DGND (GND)
          +-----+

This program (mcp3008.c) reads the analog values of the 8 input lines:

/*
 * Inspired by spidev.c
 * Copyright (c) 2007  MontaVista Software, Inc.
 * GPLv2
 */

#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 const char *device = "/dev/spidev32766.0";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;

static int read3008(int fd, int line, int bil)
{
  int ret;
  uint8_t tx[]={1,0,0};
  uint8_t rx[]={1,0,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,
  };
  tx[1] = ((bil & 1)?0x00:0x80) | ((line & 0x7)<< 4);
  ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); 
  if (ret < 1) {
    perror("can't send spi message");
    abort();
  }
  return ((rx[1]&0x3)<<8)+rx[2];
}

int main(int argc, char *argv[])
{
  int ret = 0;
  int fd;
  int i;
  fd = open(device, O_RDWR);
  if (fd < 0) {
    perror("can't open device");
    abort();
  }
  for(i=0; i<8; i++) {
    int rv=read3008(fd,i,0);
    printf("%d -> %dmV\n",rv,rv*3300/1024);
  }
  close(fd);
  return ret;
}

I have plugged all the input to GND except CH1 connected to +3.3v, this is the output:

# sudo mcp3008
0 -> 0mV
1023 -> 3296mV
0 -> 0mV
0 -> 0mV
0 -> 0mV
0 -> 0mV
0 -> 0mV
0 -> 0mV

MCP3204 has four inputs, but the conversion is more precise (12bits instead of 10).

The case is a DIP14 instead of a DIP16.

This is the connections I used for MCP3204 (should work for 3004, too).

            MCP3204       PIN Raspberry PI
            +--v--+
    in-ch0--|1  14|---VDD (+3.3V)
    in-ch1--|2  13|--Vref (+3.3V or another reference)
    in-ch2--|3  12|--AGND (GND)
    in-ch3--|4  11|---CLK (SPI CLK <-> U14 pin 28 - CSICK)
       NC --|5  10|--DOUT (SPI MISO <-> U14 pin 30 - CSIVSYNC)
       NC --|6   9|---DIN (SPI MOSI <-> U14 pin 29 - CSIHSYNC)
DGND (GND)--|7   8|----CS (SPI CS0 <-> U14 pin 27 - CSIPCK)
            +-----+

And this is the program:

/*
 * Inspired by spidev.c
 * Copyright (c) 2007  MontaVista Software, Inc.
 * GPLv2
 */

#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 const char *device = "/dev/spidev32766.0";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;

static int read3204(int fd, int line, int bil)
{ 
  int ret;
  uint8_t tx[]={(bil & 1)?0x6:0x4),line<<6,0};
  uint8_t rx[]={0,0,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) {
    perror("can't send spi message");
    abort();
  }
  return ((rx[1]&0xf)<<8)+rx[2];
}

int main(int argc, char *argv[])
{
  int ret = 0;
  int fd;
  int i;
  fd = open(device, O_RDWR);
  if (fd < 0) {
    perror("can't open device");
    abort();
  }
  for(i=0; i<4; i++) {
    int rv=read3204(fd,i,0);
    printf("%d -> %dmV\n",rv,rv*3300/4096);
  }
  close(fd);
  return ret;
}