Sign in Join
Wiki copytemporary

Raspberry Pi - SPI: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Raspberry Pi - SPI

OldNewLine
1+## Using the SPI interface on the Raspberry Pi with Free Pascal
2+
3+First of all: It is a good idea to use the pascalio Lib (https://github.com/SAmeis/pascalio) for the hardware components such as SPI, I2C and the GPIOs of the Raspberry Pi.
4+
5+However, the pascalio is also very extensive and those who want to understand what is happening may be well served with this guide.
6+
7+## SPI on Linux
8+
9+The Linux principle "Everything is a file" also applies to the SPI interface. However, the advantage that you can access the interface with simple file access is bought at a slower speed - usually uncritically - and that other programs can also access the same interface.
10+
11+On the Raspberry Pi (we're talking about version 3 with Raspbian Stretch, as of Oct 2017) there is an SPI interface that can address 2 devices: */dev/spidev0.0* and */ dev / spidev0.1* (Strictly speaking, there are several, but only one is led outside.)
12+
13+For the basics of SPI, master-slave, wiring see Wikipedia: <https://wikipedia.org/wiki/Serial_Peripheral_Interface>
14+
15+### Header
16+
17+``` pascal
18+unit test_spi;
19+
20+{$ mode objfpc} {$ H +}
21+
22+interface
23+
24+uses
25+  Classes, SysUtils, BaseUnix;
26+
27+type
28+  Tspi = class
29+
30+  procedure Init ();
31+  procedure Close ();
32+
33+  function DataIn (var din; cnt: byte): integer;
34+  function DataOut (const dout; cnt: byte): integer;
35+
36+  procedure TransferSync (const dout; var din; len: byte);
37+  procedure TransmitSync (const dout; len: byte);
38+  function FastShift (dout: byte): byte;
39+
40+  private
41+    {private declarations}
42+  public
43+    {public declarations}
44+  end;
45+
46+const
47+  // SPI mode flags
48+
49+  cSPI_CPHA = $ 01;
50+  cSPI_CPOL = $ 02;
51+
52+  cSPI_MODE_0 = (0 or 0);
53+  cSPI_MODE_1 = (0 or SPI_CPHA);
54+  cSPI_MODE_2 = (SPI_CPOL or 0);
55+  cSPI_MODE_3 = (SPI_CPOL or SPI_CPHA);
56+
57+  cSPI_CS_HIGH = $ 04;
58+  cSPI_LSB_FIRST = $ 08;
59+  cSPI_3WIRE = $ 10;
60+  cSPI_LOOP = $ 20;
61+  cSPI_NO_CS = $ 40;
62+  cSPI_READY = $ 80;
63+
64+  cSPI_CTRL = $ 6B; // is the "magic byte"
65+
66+  // control register
67+  // Read / Write + Size + MagicByte + Register
68+
69+  cSPI_RD_MODE: uint32 = $ 80016B01;
70+  cSPI_WR_MODE: uint32 = $ 40016B01;
71+  cSPI_RD_LSB_FIRST: uint32 = $ 80016B02;
72+  cSPI_WR_LSB_FIRST: uint32 = $ 40016B02;
73+  cSPI_RD_BITS_PER_WORD: uint32 = $ 80016B03;
74+  cSPI_WR_BITS_PER_WORD: uint32 = $ 40016B03;
75+  cSPI_RD_MAX_SPEED_HZ: uint32 = $ 80046B04;
76+  cSPI_WR_MAX_SPEED_HZ: uint32 = $ 40046B04;
77+
78+  cSPI_DEVICE = '/dev/spidev0.0'; // Device 0, ChipSelect 0
79+// cSPI_DEVICE = '/dev/spidev0.1'; // Device 0, ChipSelect 1
80+  cSPI_SPEED = 1000000; // data rate in Hz
81+  cSPI_BITS = 8; // data bits
82+  cSPI_LSBF = 0; // LSB first = -1
83+  cSPI_MODE = cSPI_MODE_0;
84+
85+type
86+  spi_ioc_transfer = record // Control register required for FileIO
87+    tx_buf: uint64; //pointer;
88+    rx_buf: uint64; //pointer; // always 64-bit
89+    len: uint32; // Number of characters
90+    speed: uint32; // data rate in Hz
91+    delay: uint16; // delay CS in usec
92+    bpw: uint8; // bits per word
93+    csc: uint8; // CS change
94+    txn: uint8;
95+    rxn: uint8;
96+    pad: uint16;
97+  end; // a total of 32 bytes
98+
99+var
100+  spi: Tspi;
101+  spihnd: longint; // the filehandle for the interface
102+
103+implementation
104+```
105+
106+### Initialization
107+
108+``` pascal
109+// initialize SPI
110+
111+procedure Tspi.Init ();
112+var
113+  val8: byte;
114+  val32: longword;
115+begin
116+  try
117+    spihnd: = FpOpen (cSPI_DEVICE, O_RdWr); // Open the interface for read / write
118+
119+    if spihnd <> -1 then begin
120+      val8: = cSPI_MODE;
121+      FpIOCtl (spihnd, cSPI_WR_MODE, @ val8); // set fashion
122+      val8: = cSPI_BITS;
123+      FpIOCtl (spihnd, cSPI_WR_BITS_PER_WORD, @ val8); // set data bits per byte
124+      val8: = cSPI_LSBF; //-1
125+      FpIOCtl (spihnd, cSPI_WR_LSB_FIRST, @ val8); // Set MSB or LSB first
126+      val32: = cSPI_SPEED;
127+      FpIOCtl (spihnd, cSPI_WR_MAX_SPEED_HZ, @ val32); // set speed
128+    end;
129+  finally
130+
131+  end;
132+end;
133+```
134+
135+The interface is opened when the program starts and the parameters are set. The parameters can also be changed later. For example, the clock rate can be set slow and then increased, which is necessary for some SD cards.
136+
137+*'Caution!'* The clock rate is derived from the current system clock. If the clock changes, the clock rate changes. A clock rate of 1Mhz with 600MHz system clock with higher processor load and 1200MHz system clock goes up to 2MHz.
138+
139+And: The clock rate is derived from the system clock by a divider 2^n and is never exactly correct. If you specify 1MHz, the resulting rate is somewhere around 800kHz.
140+
141+### Exit
142+
143+``` pascal
144+// exit SPI
145+
146+procedure Tspi.Close ();
147+begin
148+  if spihnd <> -1 then begin
149+    FpClose (spihnd);
150+  end;
151+end;
152+```
153+
154+Yes, at the end of the program we close the interface again.
155+
156+### Read out data
157+
158+``` pascal
159+// SPI Buffer Read
160+
161+function Tspi.DataIn (var din; cnt: byte): integer;
162+begin
163+  if spihnd <> -1 then begin
164+    DataIn: = FpRead (spihnd, din, cnt);
165+  end;
166+end;
167+```
168+
169+Since "everything is a file" we can apply a simple read to the SPI interface. This outputs the clock for the specified number of bytes and reads the response of the device in an array *din*.
170+
171+### Send data
172+
173+``` pascal
174+// SPI Buffer Write
175+
176+function Tspi.DataOut (const dout; cnt: byte): integer;
177+begin
178+  if spihnd <> -1 then begin
179+    DataOut: = FpWrite (spihnd, dout, cnt);
180+  end;
181+end;
182+```
183+
184+We can also send data with Write. The specified number of bytes is output from the *dout* array.
185+
186+### SPI Transfer
187+
188+However, most SPI devices require a slightly more complex interaction than simply sending and receiving. Often data is sent and received at the same time, and that overwhelms the capabilities of read and write. Here we need a transfer function.
189+
190+``` pascal
191+// Send, receive SPI data
192+
193+procedure Tspi.TransferSync (const dout; var din; len: byte);
194+var
195+  outbuf: array of byte;
196+  inbuf: array of byte;
197+  transfer: spi_ioc_transfer;
198+begin
199+  if len> 0 then begin
200+    SetLength (outbuf, len);
201+    FillByte (outbuf [0], len, 0);
202+    Move (dout, outbuf [0], len);
203+
204+    SetLength (inbuf, len);
205+    FillByte (inbuf [0], len, 0);
206+
207+    FillByte (transfer, SizeOf (transfer), 0);
208+    transfer.tx_buf: = uint64 (@outbuf [0]);
209+    transfer.rx_buf: = uint64 (@inbuf [0]);
210+    transfer.len: = len;
211+    transfer.delay: = 0;
212+    transfer.speed: = cSPI_SPEED;
213+    transfer.bpw: = cSPI_BITS;
214+    transfer.csc: = 0;
215+
216+    try
217+      FpIOCtl (spihnd, $ 40206B00, @transfer);
218+    finally
219+      Move (inbuf [0], din, len);
220+    end;
221+  end;
222+end;
223+```
224+
225+Now it becomes more difficult. To be able to send and receive at the same time, we need two buffers *outbuf* and *inbuf*, in which we mirror the data. We transfer their start addresses - absolutely as 64-bit values, even with a 32-bit OS - into the transfer register. The register also receives the number of bytes to be transferred and a few settings.
226+
227+Funnily enough, you can change the settings here every time, e.g. adjust the clock rate.
228+
229+We then transfer the transfer register with the settings to the file handler, which neatly scans the arrays and sends out and reads the data. The mystical *\$ 40206B00* is again a write (\$ 40), followed by the number of bytes in the transfer register (\$ 20 = 32 bytes), the magic byte (\$ 6B, see above) and the control register (\$ 00) the interface. Just leave it like that and don't fiddle with it.
230+
231+*'Disclaimer:'* This works here and now on the Raspberry Pi 3 under Raspbian. This can look different on other Linux OS and hardware platforms, especially if the byte order of the addresses is different.
232+
233+What if I only want to receive data? Then I just pass an empty array. I can also specify the same array for *dout* and *din*, then the old array data will be overwritten with the new data.
234+
235+### SPI Transmit
236+
237+``` pascal
238+// Send SPI data without receiving
239+
240+procedure Tspi.TransmitSync (const dout; len: byte);
241+var
242+  outbuf: array of byte;
243+  transfer: spi_ioc_transfer;
244+begin
245+  if len> 0 then begin
246+    SetLength (outbuf, len);
247+    FillByte (outbuf [0], len, 0);
248+    Move (dout, outbuf [0], len);
249+
250+    FillByte (transfer, SizeOf (transfer), 0);
251+    transfer.tx_buf: = uint64 (@outbuf [0]);
252+    transfer.rx_buf: = uint64 (@outbuf [0]); // same buffer
253+    transfer.len: = len;
254+    transfer.delay: = 0;
255+    transfer.speed: = cSPI_SPEED;
256+    transfer.bpw: = cSPI_BITS;
257+    transfer.csc: = 0;
258+
259+    try
260+      FpIOCtl (spihnd, $ 40206B00, @transfer);
261+    finally
262+
263+    end;
264+  end;
265+end;
266+```
267+
268+If data is only to be written, one can use the above DataOut, or this slimmed-down variant of the transfer routine.
269+
270+### SPI FastShift
271+
272+``` pascal
273+// Send, receive SPI single byte
274+
275+function Tspi.FastShift (dout: byte): byte;
276+var
277+  din: byte;
278+  transfer: spi_ioc_transfer;
279+begin
280+  din: = 0;
281+  FillByte (transfer, SizeOf (transfer), 0);
282+  transfer.tx_buf: = uint64 (@dout);
283+  transfer.rx_buf: = uint64 (@din);
284+  transfer.len: = 1;
285+  transfer.delay: = 0;
286+  transfer.speed: = cSPI_SPEED;
287+  transfer.bpw: = cSPI_BITS;
288+  transfer.csc: = 0;
289+
290+  try
291+    FpIOCtl (spihnd, $ 40206B00, @transfer);
292+  finally
293+    FastShift: = din;
294+  end;
295+end;
296+```
297+
298+If only a single byte is sent and received, for example for a status query, this version is recommended. Here the result is not in a buffer, but is returned as a function value.
299+
300+### Dismissed!
301+
302+``` pascal
303+end.
304+```
305+
306+---
307+*From the Free Pascal wiki, page "Raspberry Pi - SPI", as it stood on 2022-04-13 after a change by Mig-31, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history