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

Lines changed: 57 additions & 5 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 3 additions & 1 deletion
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy