Skip to content

Commit 56e151a

Browse files
committed
Adding documentation for threadsafe SPI.
1 parent 3b94922 commit 56e151a

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

docs/05-threadsafe-spi.md

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<img src="https://content.arduino.cc/website/Arduino_logo_teal.svg" height="100" align="right"/>
2+
3+
Threadsafe `SPI`
4+
===============
5+
## Introduction
6+
`SPI` communication shares the same problems that [have been demonstrated](04-threadsafe-wire.md) for using `Wire` in a multithreaded environment. This is due to the fact that every `SPI` transaction consists of multiple function calls, i.e.
7+
```C++
8+
digitalWrite(cs, LOW);
9+
SPI.beginTransaction(SPI_SETTINGS);
10+
rx = SPI.transfer(tx);
11+
SPI.endTransaction();
12+
digitalWrite(cs, HIGH);
13+
```
14+
Due to the preemptive nature of the underlying RTOS the execution of the `SPI` transaction can be interrupted at any point in time. This would leave both the `SPI` driver code and the client device in a undefined state. Similar as has been done for `Wire` this is solved by introducing a `BusDevice` which allows for single-function call, threadsafe, atomic SPI transactions. A `BusDevice` is declared simply by specifying the type of interface and its parameters:
15+
```C++
16+
int const DEVICE_CS_PIN = 4;
17+
void device_cs_select() { /* ... */ }
18+
void device_cs_deselect() { /* ... */ }
19+
/* ... */
20+
BusDevice bmp388(SPI, DEVICE_CS_PIN, spi_settings);
21+
/* or */
22+
BusDevice bmp388(SPI, DEVICE_CS_PIN, spi_clock, spi_bit_order, spi_bit_mode);
23+
/* or */
24+
BusDevice bmp388(SPI, device_cs_select, device_cs_deselect, spi_settings);
25+
```
26+
27+
### `transfer`/`wait` **asynchronous** threadsafe `SPI` access
28+
Once a `BusDevice` is declared it can be used to transfer data to and from the peripheral by means of the `transfer()` API. As opposed to traditional Arduino bus APIs `transfer()` is asynchronous and thus won't block execution unless the `wait()` function is called.
29+
Note that since we are in a parallel programming environment this means that calls to `transfer()` on the same bus from different sketches will be arbitrated and that the `wait()` API will suspend the sketch until the transfer is complete, thus allowing other processes to execute or going to low power state.
30+
```C++
31+
byte bmp388_read_reg(byte const reg_addr)
32+
{
33+
/* REG_ADDR | DUMMY_BYTE | REG_VAL is on SDO */
34+
byte read_write_buf[] = {static_cast<byte>(0x80 | reg_addr), 0, 0};
35+
36+
IoRequest req(read_write_buf, sizeof(read_write_buf), nullptr, 0);
37+
IoResponse rsp = bmp388.transfer(req);
38+
/* Do other stuff */
39+
rsp->wait(); /* Wait for the completion of the IO Request. */
40+
41+
return read_write_buf[2];
42+
}
43+
```
44+
45+
### `transfer_and_wait` **synchronous** threadsafe `SPI` access
46+
([`examples/Threadsafe_IO/SPI`](../examples/Threadsafe_IO/SPI))
47+
48+
As the use of the `transfer` API might be confusing there's also a synchronous API call combining the request of the transfer and waiting for it's result using `transfer_and_wait`.
49+
```C++
50+
byte bmp388_read_reg(byte const reg_addr)
51+
{
52+
/* REG_ADDR | DUMMY_BYTE | REG_VAL is on SDO */
53+
byte read_write_buf[] = {static_cast<byte>(0x80 | reg_addr), 0, 0};
54+
55+
IoRequest req(read_write_buf, sizeof(read_write_buf), nullptr, 0);
56+
IoResponse rsp = transfer_and_wait(bmp388, req);
57+
58+
return read_write_buf[2];
59+
}
60+
```
61+
62+
### `Adafruit_BusIO` style **synchronous** threadsafe `SPI` access
63+
([`examples/Threadsafe_IO/SPI_BusIO`](../examples/Threadsafe_IO/SPI_BusIO))
64+
65+
For a further simplification [Adafruit_BusIO](https://github.com/adafruit/Adafruit_BusIO) style APIs are provided:
66+
```C++
67+
byte bmp388_read_reg(byte const reg_addr)
68+
{
69+
/* REG_ADDR | DUMMY_BYTE | REG_VAL is on SDO */
70+
byte write_buf[2] = {static_cast<byte>(0x80 | reg_addr), 0};
71+
byte read_buf = 0;
72+
73+
bmp388.spi().write_then_read(write_buf, sizeof(write_buf), &read_buf, sizeof(read_buf));
74+
return read_buf;
75+
}
76+
```

docs/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ Threading on Arduino can be achieved by leveraging the [Arduino_Threads](https:/
1818
* [Data exchange between threads](02-data-exchange.md)
1919
* [Threadsafe `Serial`](03-threadsafe-serial.md)
2020
* [Threadsafe `Wire`](04-threadsafe-wire.md)
21+
* [Threadsafe `SPI`](05-threadsafe-spi.md)

0 commit comments

Comments
 (0)