Skip to content

Commit 12f0be6

Browse files
committed
Docs: Add official RingIO class.
1 parent 22a695e commit 12f0be6

File tree

3 files changed

+61
-7
lines changed

3 files changed

+61
-7
lines changed

v3/docs/INTERRUPTS.md

+57-5
Original file line numberDiff line numberDiff line change
@@ -184,17 +184,69 @@ async def process_data():
184184
await tsf.wait()
185185
# Process the data here before waiting for the next interrupt
186186
```
187+
## 3.4 micropython.RingIO
188+
189+
This is a byte-oriented circular buffer [documented here]
190+
(https://docs.micropython.org/en/latest/library/micropython.html#micropython.RingIO),
191+
which provides an efficient way to return data from an ISR to an `asyncio` task.
192+
It is implemented in C so performance is high, and supports stream I/O. The
193+
following is a usage example:
194+
```py
195+
import asyncio
196+
from machine import Timer
197+
import micropython
198+
micropython.alloc_emergency_exception_buf(100)
199+
200+
imu = SomeDevice() # Fictional hardware IMU device
201+
202+
FRAMESIZE = 8 # Count, x, y, z accel
203+
BUFSIZE = 200 # No. of records. Size allows for up to 200ms of asyncio latency.
204+
rio = micropython.RingIO(FRAMESIZE * BUFSIZE + 1) # RingIO requires an extra byte
205+
count = 0x4000 # Bit14 is "Start of frame" marker. Low bits are a frame counter.
206+
207+
def cb(_): # Timer callback. Runs at 1KHz.
208+
global count # Frame count
209+
imu.get_accel_irq() # Trigger the device
210+
rio.write(chr(count >> 8))
211+
rio.write(chr(count & 0xff))
212+
rio.write(imu.accel.ix) # Device returns bytes objects (length 2)
213+
rio.write(imu.accel.iy)
214+
rio.write(imu.accel.iz)
215+
count += 1
216+
217+
async def main(nrecs):
218+
t = Timer(freq=1_000, callback=cb)
219+
sreader = asyncio.StreamReader(rio)
220+
rpb = 100 # Records per block
221+
blocksize = FRAMESIZE * rpb
222+
with open('/sd/imudata', 'wb') as f:
223+
swriter = asyncio.StreamWriter(f, {})
224+
while nrecs:
225+
data = await sreader.readexactly(blocksize)
226+
swriter.write(data)
227+
await swriter.drain()
228+
nrecs -= rpb
229+
t.deinit()
230+
231+
asyncio.run(main(1_000))
232+
```
233+
In this example data is acquired at a timer-controlled rate of 1KHz, with eight
234+
bytes being written to the `RingIO` every tick. The `main()` task reads the data
235+
stream and writes it out to a file. Similar code was tested on a Pyboard 1.1.
187236

188-
## 3.4 Thread Safe Classes
237+
## 3.5 Other Thread Safe Classes
189238

190239
Other classes capable of being used to interface an ISR with `asyncio` are
191240
discussed [here](https://github.com/peterhinch/micropython-async/blob/master/v3/docs/THREADING.md),
192-
notably the `ThreadSafeQueue`.
241+
notably the `ThreadSafeQueue`. This ring buffer allows entries to be objects
242+
other than bytes. It supports the asynchronous iterator protocol (rather than
243+
stream I/O) and is written in Python.
193244

194245
# 4. Conclusion
195246

196-
The key take-away is that `ThreadSafeFlag` is the only official `asyncio`
197-
construct which can safely be used in an ISR context. Unofficial "thread
198-
safe" classes may also be used.
247+
The `ThreadSafeFlag` and `RingIO` classes are the official `asyncio` constructs
248+
which can safely be used in an ISR context. Unofficial "thread safe" classes may
249+
also be used. Beware of classes such as `Queue` and `RingbufQueue` which are not
250+
thread safe.
199251

200252
###### [Main tutorial](./TUTORIAL.md#contents)

v3/docs/THREADING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ instances are required.
340340
Attributes of `ThreadSafeQueue`:
341341
1. It is of fixed capacity defined on instantiation.
342342
2. It uses a pre-allocated buffer of user selectable type (`Queue` uses a
343-
dynaically allocated `list`).
343+
dynamically allocated `list`).
344344
3. It is an asynchronous iterator allowing retrieval with `async for`.
345345
4. It provides synchronous "put" and "get" methods. If the queue becomes full
346346
(put) or empty (get), behaviour is user definable. The method either blocks or

v3/primitives/ringbuf_queue.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
# API differs from CPython
77
# Uses pre-allocated ring buffer: can use list or array
88
# Asynchronous iterator allowing consumer to use async for
9-
# put_nowait QueueFull exception can be ignored allowing oldest data to be discarded.
9+
# put_nowait QueueFull exception can be ignored allowing oldest data to be discarded -
10+
# this is not thread safe, however the class as a whole is not TS because of its
11+
# use of Event objects.
1012

1113
import asyncio
1214

0 commit comments

Comments
 (0)