Difference between revisions of "Raspberry Pi - SPI/de"
Timm Thaler (talk | contribs) (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...") |
Timm Thaler (talk | contribs) |
||
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 |
− | // | ||
− | |||
− | |||
− | |||
− | |||
− | |||
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.