@@ -184,17 +184,69 @@ async def process_data():
184
184
await tsf.wait()
185
185
# Process the data here before waiting for the next interrupt
186
186
```
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 = 0x 4000 # 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 & 0x ff ))
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.
187
236
188
- ## 3.4 Thread Safe Classes
237
+ ## 3.5 Other Thread Safe Classes
189
238
190
239
Other classes capable of being used to interface an ISR with ` asyncio ` are
191
240
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.
193
244
194
245
# 4. Conclusion
195
246
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.
199
251
200
252
###### [ Main tutorial] ( ./TUTORIAL.md#contents )
0 commit comments