@@ -11,6 +11,9 @@ events.
11
11
The asynchronous ADC supports pausing a task until the value read from an ADC
12
12
goes outside defined bounds.
13
13
14
+ An IRQ_EVENT class provides a means of interfacing uasyncio to hard or soft
15
+ interrupt service routines.
16
+
14
17
# 1. Contents
15
18
16
19
1 . [ Contents] ( ./DRIVERS.md#1-contents )
@@ -24,9 +27,10 @@ goes outside defined bounds.
24
27
5 . [ ADC monitoring] ( ./DRIVERS.md#5-adc-monitoring ) Pause until an ADC goes out of bounds
25
28
5.1 [ AADC class] ( ./DRIVERS.md#51-aadc-class )
26
29
5.2 [ Design note] ( ./DRIVERS.md#52-design-note )
27
- 6 . [ Additional functions] ( ./DRIVERS.md#6-additional-functions )
28
- 6.1 [ launch] ( ./DRIVERS.md#61-launch ) Run a coro or callback interchangeably
29
- 6.2 [ set_global_exception] ( ./DRIVERS.md#62-set_global_exception ) Simplify debugging with a global exception handler
30
+ 6 . [ IRQ_EVENT] ( ./DRIVERS.md#6-irq_event )
31
+ 7 . [ Additional functions] ( ./DRIVERS.md#6-additional-functions )
32
+ 7.1 [ launch] ( ./DRIVERS.md#71-launch ) Run a coro or callback interchangeably
33
+ 7.2 [ set_global_exception] ( ./DRIVERS.md#72-set_global_exception ) Simplify debugging with a global exception handler
30
34
31
35
###### [ Tutorial] ( ./TUTORIAL.md#contents )
32
36
@@ -331,9 +335,95 @@ this for applications requiring rapid response.
331
335
332
336
###### [ Contents] ( ./DRIVERS.md#1-contents )
333
337
334
- # 6. Additional functions
338
+ # 6. IRQ_EVENT
339
+
340
+ Interfacing an interrupt service routine to ` uasyncio ` requires care. It is
341
+ invalid to issue ` create_task ` or to trigger an ` Event ` in an ISR as it can
342
+ cause a race condition in the scheduler. It is intended that ` Event ` will
343
+ become compatible with soft IRQ's in a future revison of ` uasyncio ` .
344
+
345
+ Currently there are two ways of interfacing hard or soft IRQ's with ` uasyncio ` .
346
+ One is to use a busy-wait loop as per the
347
+ [ Message] ( https://github.com/peterhinch/micropython-async/blob/master/v3/docs/TUTORIAL.md#36-message )
348
+ primitive. A more efficient approach is to use this ` IRQ_EVENT ` class. The API
349
+ is a subset of the ` Event ` class, so if official ` Event ` becomes thread-safe
350
+ it may readily be substituted. The ` IRQ_EVENT ` class uses uses the ` uasyncio `
351
+ I/O mechanism to achieve thread-safe operation.
352
+
353
+ Unlike ` Event ` only one task can wait on an ` IRQ_EVENT ` .
354
+
355
+ Constructor:
356
+ * This has no args.
357
+
358
+ Synchronous Methods:
359
+ * ` set() ` Initiates the event. May be called from a hard or soft ISR. Returns
360
+ fast.
361
+ * ` is_set() ` Returns ` True ` if the irq_event is set.
362
+ * ` clear() ` This does nothing; its purpose is to enable code to be written
363
+ compatible with a future thread-safe ` Event ` class, with the ISR setting then
364
+ immediately clearing the event.
365
+
366
+ Asynchronous Method:
367
+ * ` wait ` Pause until irq_event is set. The irq_event is cleared.
368
+
369
+ A single task waits on the event by issuing ` await irq_event.wait() ` ; execution
370
+ pauses until the ISR issues ` irq_event.set() ` . Execution of the paused task
371
+ resumes when it is next scheduled. Under current ` uasyncio ` (V3.0.0) scheduling
372
+ of the paused task does not occur any faster than using busy-wait. In typical
373
+ use the ISR services the interrupting device, saving received data, then sets
374
+ the irq_event to trigger processing of the received data.
375
+
376
+ If interrupts occur faster than ` uasyncio ` can schedule the paused task, more
377
+ than one interrupt may occur before the paused task runs.
378
+
379
+ Example usage (assumes a Pyboard with pins X1 and X2 linked):
380
+ ``` python
381
+ from machine import Pin
382
+ from pyb import LED
383
+ import uasyncio as asyncio
384
+ import micropython
385
+ from primitives.irq_event import IRQ_EVENT
386
+
387
+ micropython.alloc_emergency_exception_buf(100 )
388
+
389
+ driver = Pin(Pin.board.X2, Pin.OUT )
390
+ receiver = Pin(Pin.board.X1, Pin.IN )
391
+ evt_rx = IRQ_EVENT() # IRQ_EVENT instance for receiving Pin
392
+
393
+ def pin_han (pin ): # Hard IRQ handler. Typically services a device
394
+ evt_rx.set() # then issues this which returns quickly
395
+
396
+ receiver.irq(pin_han, Pin.IRQ_FALLING , hard = True ) # Set up hard ISR
397
+
398
+ async def pulse_gen (pin ):
399
+ while True :
400
+ await asyncio.sleep_ms(500 )
401
+ pin(not pin())
402
+
403
+ async def red_handler (evt_rx , iterations ):
404
+ led = LED(1 )
405
+ for x in range (iterations):
406
+ await evt_rx.wait() # Pause until next interrupt
407
+ print (x)
408
+ led.toggle()
409
+
410
+ async def irq_test (iterations ):
411
+ pg = asyncio.create_task(pulse_gen(driver))
412
+ await red_handler(evt_rx, iterations)
413
+ pg.cancel()
414
+
415
+ def test (iterations = 20 ):
416
+ try :
417
+ asyncio.run(irq_test(iterations))
418
+ finally :
419
+ asyncio.new_event_loop()
420
+ ```
421
+
422
+ ###### [ Contents] ( ./DRIVERS.md#1-contents )
423
+
424
+ # 7. Additional functions
335
425
336
- ## 6 .1 Launch
426
+ ## 7 .1 Launch
337
427
338
428
Importe as follows:
339
429
``` python
@@ -345,7 +435,7 @@ runs it and returns the callback's return value. If a coro is passed, it is
345
435
converted to a ` task ` and run asynchronously. The return value is the ` task `
346
436
instance. A usage example is in ` primitives/switch.py ` .
347
437
348
- ## 6 .2 set_global_exception
438
+ ## 7 .2 set_global_exception
349
439
350
440
Import as follows:
351
441
``` python
0 commit comments