Difference between revisions of "Raspberry Pi - SPI/de"

From Free Pascal wiki
Jump to navigationJump to search
(Created page with "==Nutzung der SPI-Schnittstelle am Raspberry Pi mit Freepascal== Gleich vorweg: Es ist eine gute Idee, für die Hardwarekomponenten wie SPI, I2C und GPIOs des Raspberry Pi di...")
 
Line 76: Line 76:
 
   cSPI_WR_MAX_SPEED_HZ  : uint32 = $40046B04;
 
   cSPI_WR_MAX_SPEED_HZ  : uint32 = $40046B04;
  
   cSPI_DEVICE = '/dev/spidev0.1';  // Device 0, ChipSelect 1
+
   cSPI_DEVICE = '/dev/spidev0.0';  // Device 0, ChipSelect 0
 +
//  cSPI_DEVICE = '/dev/spidev0.1';  // Device 0, ChipSelect 1
 
   cSPI_SPEED  = 1000000;  // Datenrate in Hz
 
   cSPI_SPEED  = 1000000;  // Datenrate in Hz
 
   cSPI_BITS  = 8;  // Datenbits
 
   cSPI_BITS  = 8;  // Datenbits
Line 83: Line 84:
  
 
type
 
type
   spi_ioc_transfer = record
+
   spi_ioc_transfer = record // Controlregister für FileIO benötigt
 
     tx_buf : uint64;  //pointer;
 
     tx_buf : uint64;  //pointer;
 
     rx_buf : uint64;  //pointer;  // immer 64-bit
 
     rx_buf : uint64;  //pointer;  // immer 64-bit
Line 94: Line 95:
 
     rxn : uint8;
 
     rxn : uint8;
 
     pad : uint16;
 
     pad : uint16;
   end;
+
   end; // ingesamt 32 Byte
  
 
var
 
var
 
   spi : Tspi;
 
   spi : Tspi;
   spihnd : longint;
+
   spihnd : longint; // der filehandle für die Schnittstelle
  
 
implementation
 
implementation
 
</syntaxhighlight>
 
</syntaxhighlight>
  
====
+
==Initialsierung==
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Line 114: Line 115:
 
begin
 
begin
 
   try
 
   try
     spihnd := FpOpen(cSPI_DEVICE, O_RdWr);
+
     spihnd := FpOpen(cSPI_DEVICE, O_RdWr); // Schnittstelle für Read/Write öffnen
  
 
     if spihnd <> -1 then begin
 
     if spihnd <> -1 then begin
 
       val8 := cSPI_MODE;
 
       val8 := cSPI_MODE;
       FpIOCtl(spihnd, cSPI_WR_MODE, @val8);
+
       FpIOCtl(spihnd, cSPI_WR_MODE, @val8); // Mode setzen
 
       val8 := cSPI_BITS;
 
       val8 := cSPI_BITS;
       FpIOCtl(spihnd, cSPI_WR_BITS_PER_WORD, @val8);
+
       FpIOCtl(spihnd, cSPI_WR_BITS_PER_WORD, @val8); // Datenbits per Byte setzen
 
       val8 := cSPI_LSBF;  //-1
 
       val8 := cSPI_LSBF;  //-1
       FpIOCtl(spihnd, cSPI_WR_LSB_FIRST, @val8);
+
       FpIOCtl(spihnd, cSPI_WR_LSB_FIRST, @val8); // MSB oder LSB first setzen
 
       val32 := cSPI_SPEED;
 
       val32 := cSPI_SPEED;
       FpIOCtl(spihnd, cSPI_WR_MAX_SPEED_HZ, @val32);
+
       FpIOCtl(spihnd, cSPI_WR_MAX_SPEED_HZ, @val32); // Speed setzen
//   FpIOCtl(spihnd, cSPI_RD_MODE, @val8);
 
//    WriteLn('Mode ', IntToHex(val8, 2));
 
//    FpIOCtl(spihnd, cSPI_RD_BITS_PER_WORD, @val8);
 
//    WriteLn('Bits ', val8);
 
//    FpIOCtl(spihnd, cSPI_RD_MAX_SPEED_HZ, @val32);
 
//    WriteLn('Speed ', val32);
 
 
     end;
 
     end;
 
   finally
 
   finally
Line 136: Line 131:
 
   end;
 
   end;
 
end;
 
end;
 +
</syntaxhighlight>
 +
 +
Die Schnittstelle wird beim Programmstart geöffnet und die Parameter eingestellt. Die Parameter können auch später noch geändert werden. Zum Beispiel kann die Clockrate erst langsam gesetzt und dann erhöht werden, was für einige SD-Karten nötig ist.
 +
 +
'''Achtung!''' Die Clockrate wird aus dem momentanen Systemtakt abgeleitet. Ändert sich der Takt, ändert sich die Taktrate. So geht eine Clockrate von 1Mhz bei 600MHz Systemtakt bei höherer Prozessorlast und 1200MHz Systemtakt auf 2MHz hoch.
 +
 +
Und: Die Clockrate wird durch einen Teiler 2^n aus dem Systemtakt abgeleitet und stimmt nie genau. Wenn man 1MHz angibt, liegt die resultierende Rate irgendwo bei 800kHz.
 +
 +
==Beenden==
  
 +
<syntaxhighlight lang="pascal">
 
// SPI beenden
 
// SPI beenden
  
Line 145: Line 150:
 
   end;
 
   end;
 
end;
 
end;
 +
</syntaxhighlight>
 +
 +
Ja, am Programmende machen wir die Schnittstelle brav wieder zu.
 +
 +
==Daten auslesen==
  
 +
<syntaxhighlight lang="pascal">
 
// SPI Buffer Read
 
// SPI Buffer Read
  
Line 154: Line 165:
 
   end;
 
   end;
 
end;
 
end;
 +
</syntaxhighlight>
  
 +
Da "Alles eine Datei" ist, können wir ein einfaches Read auf die SPI-Schnittstelle anwenden. Das gibt den Takt für die angegebene Anzahl Bytes aus und liest die Antwort des Devices in ein Array ''din''.
 +
 +
==Daten senden==
 +
 +
<syntaxhighlight lang="pascal">
 
// SPI Buffer Write
 
// SPI Buffer Write
  
Line 163: Line 180:
 
   end;
 
   end;
 
end;
 
end;
 +
</syntaxhighlight>
  
 +
 +
 +
==Daten senden==
 +
 +
<syntaxhighlight lang="pascal">
 
// SPI Daten senden, empfangen
 
// SPI Daten senden, empfangen
  

Revision as of 00:26, 22 October 2017

Nutzung der SPI-Schnittstelle am Raspberry Pi mit Freepascal

Gleich vorweg: Es ist eine gute Idee, für die Hardwarekomponenten wie SPI, I2C und GPIOs des Raspberry Pi die pascalio Lib (https://github.com/SAmeis/pascalio) zu verwenden.

Allerdings ist die pascalio auch sehr umfangreich und wer verstehen möchte, was da passiert, ist vielleicht mit dieser Anleitung ganz gut bedient.

SPI unter Linux

Auch für die SPI-Schnittstelle gilt das Linux-Prinzip "Alles ist eine Datei". Den Vorteil, dass man mit einfachen Dateizugriffen auf die Schnittstelle zugreifen kann erkauft man sich allerdings mit geringerer Geschwindigkeit - meist unkritisch - und damit, dass auch andere Programme auf die gleiche Schnittstelle zugreifen können.

Auf dem Raspberry Pi (wir reden hier von Version 3 mit Raspbian Stretch, Stand Okt 2017) gibt es eine SPI-Schnittstelle, die 2 Devices ansprechen kann: /dev/spidev0.0 und /dev/spidev0.1

Zu den Grundlagen von SPI, Master-Slave, Verdrahtung siehe Wikipedia: https://de.wikipedia.org/wiki/Serial_Peripheral_Interface

Header

unit test_spi;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, BaseUnix, UnixType;

type
  Tspi = class

  procedure Init();
  procedure Close();

  function DataIn(var din; cnt : integer) : integer;
  function DataOut(const dout; cnt : integer) : integer;

  procedure TransferSync(const dout; var din; len : integer);
  procedure TransmitSync(const dout; len : integer);
  function FastShift(dout : shortint) : shortint;

  private
    { private declarations }
  public
    { public declarations }
  end;

const
  // SPI Mode Flags

  cSPI_CPHA = $01;
  cSPI_CPOL = $02;  

  cSPI_MODE_0 = (0 or 0);
  cSPI_MODE_1 = (0 or SPI_CPHA);
  cSPI_MODE_2 = (SPI_CPOL or 0);
  cSPI_MODE_3 = (SPI_CPOL or SPI_CPHA);

  cSPI_CS_HIGH   = $04;
  cSPI_LSB_FIRST = $08;
  cSPI_3WIRE     = $10;
  cSPI_LOOP      = $20;
  cSPI_NO_CS     = $40;
  cSPI_READY     = $80;

  cSPI_CTRL = $6B;  // ist das "Magic Byte"

  // Controlregister
  // Read/Write + Size + MagicByte + Register

  cSPI_RD_MODE          : uint32 = $80016B01;
  cSPI_WR_MODE          : uint32 = $40016B01;
  cSPI_RD_LSB_FIRST     : uint32 = $80016B02;
  cSPI_WR_LSB_FIRST     : uint32 = $40016B02;
  cSPI_RD_BITS_PER_WORD : uint32 = $80016B03;
  cSPI_WR_BITS_PER_WORD : uint32 = $40016B03;
  cSPI_RD_MAX_SPEED_HZ  : uint32 = $80046B04;
  cSPI_WR_MAX_SPEED_HZ  : uint32 = $40046B04;

  cSPI_DEVICE = '/dev/spidev0.0';  // Device 0, ChipSelect 0
//  cSPI_DEVICE = '/dev/spidev0.1';  // Device 0, ChipSelect 1
  cSPI_SPEED  = 1000000;  // Datenrate in Hz
  cSPI_BITS   = 8;  // Datenbits
  cSPI_LSBF   = 0;  // LSB first = -1
  cSPI_MODE   = cSPI_MODE_0;

type
  spi_ioc_transfer = record  // Controlregister für FileIO benötigt
    tx_buf : uint64;  //pointer;
    rx_buf : uint64;  //pointer;  // immer 64-bit
    len : uint32;    // Anzahl Zeichen
    speed : uint32;  // Datenrate in Hz
    delay : uint16;  // Verzögerung CS in usec
    bpw : uint8;  // Bits per Word
    csc : uint8;  // CS change
    txn : uint8;
    rxn : uint8;
    pad : uint16;
  end;  // ingesamt 32 Byte

var
  spi : Tspi;
  spihnd : longint;  // der filehandle für die Schnittstelle

implementation

Initialsierung

 // SPI initialisieren

procedure Tspi.Init();
var
  val8 : shortint;
  val32 : longint;
begin
  try
    spihnd := FpOpen(cSPI_DEVICE, O_RdWr);  // Schnittstelle für Read/Write öffnen

    if spihnd <> -1 then begin
      val8 := cSPI_MODE;
      FpIOCtl(spihnd, cSPI_WR_MODE, @val8);  // Mode setzen
      val8 := cSPI_BITS;
      FpIOCtl(spihnd, cSPI_WR_BITS_PER_WORD, @val8);  // Datenbits per Byte setzen
      val8 := cSPI_LSBF;  //-1
      FpIOCtl(spihnd, cSPI_WR_LSB_FIRST, @val8);  // MSB oder LSB first setzen
      val32 := cSPI_SPEED;
      FpIOCtl(spihnd, cSPI_WR_MAX_SPEED_HZ, @val32);  // Speed setzen
    end;
  finally

  end;
end;

Die Schnittstelle wird beim Programmstart geöffnet und die Parameter eingestellt. Die Parameter können auch später noch geändert werden. Zum Beispiel kann die Clockrate erst langsam gesetzt und dann erhöht werden, was für einige SD-Karten nötig ist.

Achtung! Die Clockrate wird aus dem momentanen Systemtakt abgeleitet. Ändert sich der Takt, ändert sich die Taktrate. So geht eine Clockrate von 1Mhz bei 600MHz Systemtakt bei höherer Prozessorlast und 1200MHz Systemtakt auf 2MHz hoch.

Und: Die Clockrate wird durch einen Teiler 2^n aus dem Systemtakt abgeleitet und stimmt nie genau. Wenn man 1MHz angibt, liegt die resultierende Rate irgendwo bei 800kHz.

Beenden

// SPI beenden

procedure Tspi.Close();
begin
  if spihnd <> -1 then begin
    FpClose(spihnd);
  end;
end;

Ja, am Programmende machen wir die Schnittstelle brav wieder zu.

Daten auslesen

// SPI Buffer Read

function Tspi.DataIn(var din; cnt : integer) : integer;
begin
  if spihnd <> -1 then begin
    DataIn := FpRead(spihnd, din, cnt);
  end;
end;

Da "Alles eine Datei" ist, können wir ein einfaches Read auf die SPI-Schnittstelle anwenden. Das gibt den Takt für die angegebene Anzahl Bytes aus und liest die Antwort des Devices in ein Array din.

Daten senden

// SPI Buffer Write

function Tspi.DataOut(const dout; cnt : integer) : integer;
begin
  if spihnd <> -1 then begin
    DataOut := FpWrite(spihnd, dout, cnt);
  end;
end;


Daten senden

// SPI Daten senden, empfangen

procedure Tspi.TransferSync(const dout; var din; len : integer);
var
  status : integer;
  outbuf : array of shortint;
  inbuf : array of shortint;
  transfer : spi_ioc_transfer;
begin
  if len > 0 then begin
    SetLength(outbuf, len);
    FillByte(outbuf[0], len, 0);
    Move(dout, outbuf[0], len);

    SetLength(inbuf, len);
    FillByte(inbuf[0], len, 0);

    FillByte(transfer, SizeOf(transfer), 0);
    transfer.tx_buf := uint64(@outbuf[0]);
    transfer.rx_buf := uint64(@inbuf[0]);
    transfer.len := len;
    transfer.delay := 0;
    transfer.speed := cSPI_SPEED;
    transfer.bpw := cSPI_BITS;
    transfer.csc := 0;

    try
      FpIOCtl(spihnd, $40206B00, @transfer);
    finally
      Move(inbuf[0], din, len);
    end;
  end;
end;

// SPI Daten senden ohne Empfang          strom

procedure Tspi.TransmitSync(const dout; len : integer);
var
  status : integer;
  outbuf : array of shortint;
//  inbuf : array of shortint;
  transfer : spi_ioc_transfer;
begin
  if len > 0 then begin
    SetLength(outbuf, len);
    FillByte(outbuf[0], len, 0);
    Move(dout, outbuf[0], len);

//    SetLength(inbuf, 1);
//    FillByte(inbuf[0], len, 0);

    FillByte(transfer, SizeOf(transfer), 0);
    transfer.tx_buf := uint64(@outbuf[0]);
    transfer.rx_buf := uint64(@outbuf[0]);  // gleicher Buffer
    transfer.len := len;
    transfer.delay := 0;
    transfer.speed := cSPI_SPEED;
    transfer.bpw := cSPI_BITS;
    transfer.csc := 0;

    try
      FpIOCtl(spihnd, $40206B00, @transfer);
    finally

    end;
  end;
end;

// SPI einzelnes Byte senden, empfangen

function Tspi.FastShift(dout : shortint) : shortint;
var
  din : shortint;
  transfer : spi_ioc_transfer;
begin
  din := 0;
  FillByte(transfer, SizeOf(transfer), 0);
  transfer.tx_buf := uint64(@dout);
  transfer.rx_buf := uint64(@din);
  transfer.len := 1;
  transfer.delay := 0;
  transfer.speed := cSPI_SPEED;
  transfer.bpw := cSPI_BITS;
  transfer.csc := 0;

  try
    FpIOCtl(spihnd, $40206B00, @transfer);
  finally
    FastShift := din;
  end;
end;

end.