Skip to content

Commit 1bc4247

Browse files
committed
Adding documentation for threadsafe Wire.
1 parent 3372d4b commit 1bc4247

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

docs/04-threadsafe-wire.md

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<img src="https://content.arduino.cc/website/Arduino_logo_teal.svg" height="100" align="right"/>
2+
3+
Threadsafe `Wire`
4+
=================
5+
## Introduction
6+
A key problem of multi-tasking is the prevention of erroneous state when multiple threads share a single resource. The following example borrowed from a typical application demonstrates the problems resulting from multiple threads sharing a single resource:
7+
8+
Imagine a embedded system where multiple `Wire` client devices are physically connected to a single `Wire` server. Each `Wire` client device is managed by a dedicated software thread. Each thread polls its `Wire` client device periodically. Access to the `Wire` bus is managed via the `Wire` library and typically follows the pattern described below:
9+
```C++
10+
/* Wire Write Access */
11+
Wire.beginTransmission(addr);
12+
Wire.write(val);
13+
Wire.endTransmission();
14+
15+
/* Wire Read Access */
16+
Wire.beginTransmission(addr);
17+
Wire.write(val);
18+
Wire.endTransmission();
19+
Wire.requestFrom(addr, bytes)
20+
while(Wire.available()) {
21+
int val = Wire.read();
22+
}
23+
```
24+
Since we are using a [preemptive](https://os.mbed.com/docs/mbed-os/v6.11/program-setup/concepts.html#threads) [RTOS](https://en.wikipedia.org/wiki/Real-time_operating_system) [ARM Mbed OS](https://os.mbed.com/mbed-os/) with a tick time of 10 ms for achieving multi-tasking capability and under the assumption that all threads share the same priority (which leads to a [round-robin scheduling](https://en.wikipedia.org/wiki/Round-robin_scheduling)) it can easily happen that one thread is half-way through its `Wire` access when the scheduler interrupts it and schedules the next thread which in turn starts/continues/ends its own `Wire` access.
25+
26+
As a result this interruption by the scheduler will break `Wire` access for both devices and leave the `Wire` controller in an undefined state.
27+
28+
In Arduino Parallela we introduced the concept of `BusDevice`s which are meant to unify the way sketches access peripherals through heterogeneous busses such as `Wire`, `SPI` and `Serial`. A `BusDevice` is declared simply by specifying the type of interface and its parameters:
29+
```C++
30+
BusDevice lsm6dsox(Wire, LSM6DSOX_ADDRESS);
31+
/* or */
32+
BusDevice lsm6dsox(Wire, LSM6DSOX_ADDRESS, true /* restart */);
33+
/* or */
34+
BusDevice lsm6dsox(Wire, LSM6DSOX_ADDRESS, false /* restart */, true, /* stop */);
35+
```
36+
37+
### `transfer`/`wait` **asynchronous** threadsafe `Wire` access
38+
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.
39+
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.
40+
```C++
41+
byte lsm6dsox_read_reg(byte const reg_addr)
42+
{
43+
byte write_buf = reg_addr;
44+
byte read_buf = 0;
45+
46+
IoRequest req(write_buf, read_buf);
47+
IoResponse rsp = lsm6dsox.transfer(req);
48+
/* Do other stuff */
49+
rsp->wait(); /* Wait for the completion of the IO Request. */
50+
51+
return read_buf;
52+
}
53+
```
54+
55+
### `transfer_and_wait` **synchronous** threadsafe `Wire` access
56+
([`examples/Threadsafe_IO/Wire`](../examples/Threadsafe_IO/Wire))
57+
58+
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`.
59+
```C++
60+
byte lsm6dsox_read_reg(byte const reg_addr)
61+
{
62+
byte write_buf = reg_addr;
63+
byte read_buf = 0;
64+
65+
IoRequest req(write_buf, read_buf);
66+
IoResponse rsp = transfer_and_wait(lsm6dsox, req); /* Transmit IO request for execution and wait for completion of request. */
67+
68+
return read_buf;
69+
}
70+
```
71+
72+
### `Adafruit_BusIO` style **synchronous** threadsafe `Wire` access
73+
([`examples/Threadsafe_IO/Wire_BusIO`](../examples/Threadsafe_IO/Wire_BusIO))
74+
75+
For a further simplification [Adafruit_BusIO](https://github.com/adafruit/Adafruit_BusIO) style APIs are provided:
76+
```C++
77+
byte lsm6dsox_read_reg(byte reg_addr)
78+
{
79+
byte read_buf = 0;
80+
lsm6dsox.wire().write_then_read(&reg_addr, 1, &read_buf, 1);
81+
return read_buf;
82+
}
83+
```

docs/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ Threading on Arduino can be achieved by leveraging the [Arduino_Threads](https:/
1515
* [Threading Basics](01-threading-basics.md)
1616
* [Data exchange between threads](02-data-exchange.md)
1717
* [Threadsafe `Serial`](03-threadsafe-serial.md)
18+
* [Threadsafe `Wire`](04-threadsafe-wire.md)

0 commit comments

Comments
 (0)