Homepage Wiki Forum Buy

SPI

Aus GNUBLIN

Schwierigkeitsgrad Voraussetzung Gnublin Familie
Gnublin logo advanced.png SPI Wissen, Anschluss Bauteil an Schnittstelle Alle

Inhaltsverzeichnis

SPI Schnittstelle

Die SPI-Schnittstelle [1] ist eine einfache Möglichkeit externe Bausteine anzuschließen. Mittels einer gemeinsamen Taktleitung und einer für den seriellen Empfang und eine für das serielle Versenden von Daten können einfach meist 8 Bit Blöcke übertragen werden.

Technische Daten

  • Motorola SPI frame Format mit einer Wortgröße von 8/16 bits
  • Texas Instruments SSI Frame Format mit einer Wotgröße von 4 bis 16 bits
  • Serial Taktrate im Master Modus bis 45 MHz
  • Serial Taktrate im Slave Modus bis 25 MHz
  • Support for single data access DMA
  • Full-Duplex operation
  • Maskable interrupts


Die SPI Schnittstelle

Gnublin board spi1.jpg

Im Bild sieht man links unten einen 14-Pol Anschluß von GNUBLIN. In der nächsten Zeichnung wird dieser etwas genauer erläutert.


Gnublin board spi new.png

Am GNUBLIN sind für die SPI Schnittstelle folgende 4 Pins notwendig:

3 - GPIO11 (Dieser Pin ist der Chipselect Pin)
5 - SCK    (Taktleitung für SPI)
7 - MISO   (Master in Slave out)
8 - MOSI   (Master out Slave in)


Die Schnittstellen der einzelnen GNUBLIN Bauformen und deren Position können hier angesehen werden.


Bei manchen SPI Slaves kann es auch sein, dass der Chipselect Pin des Slaves komplett auf LOW (Masse) gezogen werden kann. Jedoch geht das nur, wenn man einen Slave am SPI Bus hängt.

Beim Standard GNUBLIN kann man immer nur ein SPI Gerät einzeln betreiben, da die CS und IRQ Leitung auf GPIO11 und GPIO14 gelegt sind. Pro Gerät braucht man eine eigene CS und IRQ Leitung (z.T.).


Konfiguration im Kernel

Um alle Funktionen nutzen zu können sollte der gnublin Community Kernel verwendet werden.(git clone http://code.google.com/p/gnublin-develop-kernel)

Der SPI Treiber ist im Kernel schon fest mit einkompiliert. Wenn der Treiber funktioniert, dann sollte ein spidev Attribut unter /sys/class/spidev zu finden sein:

modprobe spidev
ls /sys/class/spidev/
spidev0.11

Hier sieht man z.B. das spidev angelegt (Mit Busnummer = 0 und Chipselect = 11) worden ist und der Treiber funktioniert.

Es wird auch eine Gerätedatei unter /dev/spidev0.11 angelegt.

Nachtrag: Um den Treiber schnell testen zu können, bzw schnell mal einen Wert über die Schnittstelle zu senden kann man mit dem Befehl echo auf die Gerätedatei zugreifen und dadurch spielend leicht Daten versenden.

echo 100 > /dev/spidev0.11


Ansteuerungs Beispiel in C

Im folgenden sieht man ein kleines Ansteuerungs Beispiel in C. Wenn der Treiber richtig geladen worden ist, muss man noch ein devicefile anlegen. Die Variable *device legt den Namen des devicefiles fest, d.h. das Programm wird immer auf das angegebene devicefile zugreifen.

Ist das devicefile noch nicht im Ordner /dev/ zu finden so muss man dies noch anlegen:

mknod /dev/spidev0.11 c 153 0

Das Programm starten

Um auf die SPI Schnittstelle direkt zugreifen zu können benötigt man das Module spidev

Hinweis: Alle SPI Treiber wie CAN, ENC28J60, etc. müssen zuvor entladen sein! (z.B. modprobe -r enc28j60)

modprobe spidev


Danach kann man das Beispielprogramm in examples verwenden

./root/examples/misc/spi/spidev_test2

Bzw. kann man es auch selber übersetzen:

gcc -o /root/examples/misc/spi/spidev_test2 /root/examples/misc/spi/spidev_test2.c


#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/spi0";  // <-- Changes here
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 100000;
static uint16_t delay;
 
static void transfer(int fd)
{
   int ret;
   uint8_t tx[] = {
      0xF0, 0xAA, 0x0F, 0xAA, 0x55,
   };
   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("");
}
 
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);
}
 
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' },
         { NULL, 0, 0, 0 },
      };
      int c;
 
      c = getopt_long(argc, argv, "D:s:d:b:lHOLC3", 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;
      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;
}


Tek-spi.jpg

Dieses Oszilloskopbild zeigt das Beispielprogramm , das die fünf Byte 0xF0, 0xAA, 0x0F, 0xAA und 0x55 auf die SPI Schnittstelle schreibt. Der obere Kanal ist der Takt SCK, der untere Kanal ist das MOSI Signal. Der MISO Eingang blieb offen, deswegen haben alle eingelesenen Bytes den Wert 0xFF.


HINWEIS: Ist man im besitz der Gnublin Distribution so ist das Beispielprogramm auf dem rootfs mit drauf (Im root Ordner).

In anderen Sprachen