1
1
# Synopsis
2
2
3
- Using ` Event ` instances rather than callbacks in ` uasyncio ` device drivers can
3
+ Using ` Event ` instances rather than callbacks in ` asyncio ` device drivers can
4
4
simplify their design and standardise their APIs. It can also simplify
5
5
application logic.
6
6
7
- This document assumes familiarity with ` uasyncio ` . See [ official docs] ( http://docs.micropython.org/en/latest/library/uasyncio .html ) and
7
+ This document assumes familiarity with ` asyncio ` . See [ official docs] ( http://docs.micropython.org/en/latest/library/asyncio .html ) and
8
8
[ unofficial tutorial] ( https://github.com/peterhinch/micropython-async/blob/master/v3/docs/TUTORIAL.md ) .
9
9
10
10
# 0. Contents
11
11
12
- 1 . [ An alternative to callbacks in uasyncio code] ( ./EVENTS.md#1-an-alternative-to-callbacks-in-uasyncio -code )
12
+ 1 . [ An alternative to callbacks in asyncio code] ( ./EVENTS.md#1-an-alternative-to-callbacks-in-asyncio -code )
13
13
2 . [ Rationale] ( ./EVENTS.md#2-rationale )
14
14
3 . [ Device driver design] ( ./EVENTS.md#3-device-driver-design )
15
15
4 . [ Primitives] ( ./EVENTS.md#4-primitives ) Facilitating Event-based application logic
@@ -25,10 +25,11 @@ This document assumes familiarity with `uasyncio`. See [official docs](http://do
25
25
6.2 [ EButton] ( ./EVENTS.md#62-ebutton ) Debounced pushbutton with double and long press events
26
26
  ;  ;  ;  ;  ; 6.2.1 [ The suppress constructor argument] ( ./EVENTS.md#621-the-suppress-constructor-argument )
27
27
  ;  ;  ;  ;  ; 6.2.2 [ The sense constructor argument] ( ./EVENTS.md#622-the-sense-constructor-argument )
28
+ 6.3 [ Keyboard] ( ./EVENTS.md#63-keyboard ) A crosspoint array of pushbuttons.
28
29
7 . [ Ringbuf queue] ( ./EVENTS.md#7-ringbuf-queue ) A MicroPython optimised queue primitive.
29
30
[ Appendix 1 Polling] ( ./EVENTS.md#100-appendix-1-polling )
30
31
31
- # 1. An alternative to callbacks in uasyncio code
32
+ # 1. An alternative to callbacks in asyncio code
32
33
33
34
Callbacks have two merits. They are familiar, and they enable an interface
34
35
which allows an asynchronous application to be accessed by synchronous code.
@@ -49,7 +50,7 @@ async def handle_messages(input_stream):
49
50
Callbacks are not a natural fit in this model. Viewing the declaration of a
50
51
synchronous function, it is not evident how the function gets called or in what
51
52
context the code runs. Is it an ISR? Is it called from another thread or core?
52
- Or is it a callback running in a ` uasyncio ` context? You cannot tell without
53
+ Or is it a callback running in a ` asyncio ` context? You cannot tell without
53
54
trawling the code. By contrast, a routine such as the above example is a self
54
55
contained process whose context and intended behaviour are evident.
55
56
@@ -93,15 +94,15 @@ know to access this driver interface is the name of the bound `Event`.
93
94
This doc aims to demostrate that the event based approach can simplify
94
95
application logic by eliminating the need for callbacks.
95
96
96
- The design of ` uasyncio ` V3 and its ` Event ` class enables this approach
97
+ The design of ` asyncio ` V3 and its ` Event ` class enables this approach
97
98
because:
98
99
1 . A task waiting on an ` Event ` is put on a queue where it consumes no CPU
99
100
cycles until the event is triggered.
100
- 2 . The design of ` uasyncio ` can support large numbers of tasks (hundreds) on
101
+ 2 . The design of ` asyncio ` can support large numbers of tasks (hundreds) on
101
102
a typical microcontroller. Proliferation of tasks is not a problem, especially
102
103
where they are small and spend most of the time paused waiting on queues.
103
104
104
- This contrasts with other schedulers (such as ` uasyncio ` V2) where there was no
105
+ This contrasts with other schedulers (such as ` asyncio ` V2) where there was no
105
106
built-in ` Event ` class; typical ` Event ` implementations used
106
107
[ polling] ( ./EVENTS.md#100-appendix-1-polling ) and were convenience objects
107
108
rather than performance solutions.
@@ -151,7 +152,7 @@ Drivers exposing `Event` instances include:
151
152
152
153
Applying ` Events ` to typical logic problems requires two new primitives:
153
154
` WaitAny ` and ` WaitAll ` . Each is an ELO. These primitives may be cancelled or
154
- subject to a timeout with ` uasyncio .wait_for()` , although judicious use of
155
+ subject to a timeout with ` asyncio .wait_for()` , although judicious use of
155
156
` Delay_ms ` offers greater flexibility than ` wait_for ` .
156
157
157
158
## 4.1 WaitAny
@@ -325,13 +326,16 @@ async def foo():
325
326
326
327
This document describes drivers for mechanical switches and pushbuttons. These
327
328
have event based interfaces exclusively and support debouncing. The drivers are
328
- simplified alternatives for
329
+ simplified alternatives for
329
330
[ Switch] ( https://github.com/peterhinch/micropython-async/blob/master/v3/primitives/switch.py )
330
331
and [ Pushbutton] ( https://github.com/peterhinch/micropython-async/blob/master/v3/primitives/pushbutton.py ) ,
331
332
which also support callbacks.
332
333
333
334
## 6.1 ESwitch
334
335
336
+ ``` python
337
+ from primitives import ESwitch
338
+ ```
335
339
This provides a debounced interface to a switch connected to gnd or to 3V3. A
336
340
pullup or pull down resistor should be supplied to ensure a valid logic level
337
341
when the switch is open. The default constructor arg ` lopen=1 ` is for a switch
@@ -348,7 +352,7 @@ Constructor arguments:
348
352
down as appropriate.
349
353
2 . ` lopen=1 ` Electrical level when switch is open circuit i.e. 1 is 3.3V, 0 is
350
354
gnd.
351
-
355
+
352
356
Methods:
353
357
354
358
1 . ` __call__ ` Call syntax e.g. ` myswitch() ` returns the logical debounced
@@ -363,7 +367,7 @@ Bound objects:
363
367
Application code is responsible for clearing the ` Event ` instances.
364
368
Usage example:
365
369
``` python
366
- import uasyncio as asyncio
370
+ import asyncio
367
371
from machine import Pin
368
372
from primitives import ESwitch
369
373
es = ESwitch(Pin(" Y1" , Pin.IN , Pin.PULL_UP ))
@@ -390,7 +394,11 @@ asyncio.run(main())
390
394
###### [ Contents] ( ./EVENTS.md#0-contents )
391
395
392
396
## 6.2 EButton
393
-
397
+
398
+ ``` python
399
+ from primitives import EButton
400
+ ```
401
+
394
402
This extends the functionality of ` ESwitch ` to provide additional events for
395
403
long and double presses.
396
404
@@ -479,12 +487,63 @@ determine whether the button is closed or open.
479
487
480
488
###### [ Contents] ( ./EVENTS.md#0-contents )
481
489
490
+ ## 6.3 Keyboard
491
+
492
+ ``` python
493
+ from primitives import Keyboard
494
+ ```
495
+ A ` Keyboard ` provides an interface to a set of pushbuttons arranged as a
496
+ crosspoint array. If a key is pressed its array index (scan code) is placed on a
497
+ queue. Keypresses are retrieved with ` async for ` . The driver operates by
498
+ polling each row, reading the response of each column. N-key rollover is
499
+ supported - this is the case where a key is pressed before the prior key has
500
+ been released.
501
+
502
+ Example usage:
503
+ ``` python
504
+ import asyncio
505
+ from primitives import Keyboard
506
+ from machine import Pin
507
+ rowpins = [Pin(p, Pin.OUT ) for p in range (10 , 14 )]
508
+ colpins = [Pin(p, Pin.IN , Pin.PULL_DOWN ) for p in range (16 , 20 )]
509
+
510
+ async def main ():
511
+ kp = Keyboard(rowpins, colpins)
512
+ async for scan_code in kp:
513
+ print (scan_code)
514
+ if not scan_code:
515
+ break # Quit on key with code 0
516
+
517
+ asyncio.run(main())
518
+ ```
519
+ Constructor mandatory args:
520
+ * ` rowpins ` A list or tuple of initialised output pins.
521
+ * ` colpins ` A list or tuple of initialised input pins (pulled down).
522
+ Constructor optional keyword only args:
523
+ * ` buffer=bytearray(10) ` Keyboard buffer.
524
+ * ` db_delay=50 ` Debounce delay in ms.
525
+
526
+ The ` Keyboard ` class is subclassed from [ Ringbuf queue] ( ./EVENTS.md#7-ringbuf-queue )
527
+ enabling scan codes to be retrieved with an asynchronous iterator.
528
+
529
+ In typical use the scan code would be used as the index into a string of
530
+ keyboard characters ordered to match the physical layout of the keys. If data
531
+ is not removed from the buffer, on overflow the oldest scan code is discarded.
532
+ There is no limit on the number of rows or columns however if more than 256 keys
533
+ are used, the ` buffer ` arg would need to be adapted to handle scan codes > 255.
534
+
535
+ ###### [ Contents] ( ./EVENTS.md#0-contents )
536
+
482
537
# 7. Ringbuf Queue
483
538
539
+ ``` python
540
+ from primitives import RingbufQueue
541
+ ```
542
+
484
543
The API of the ` Queue ` aims for CPython compatibility. This is at some cost to
485
544
efficiency. As the name suggests, the ` RingbufQueue ` class uses a pre-allocated
486
545
circular buffer which may be of any mutable type supporting the buffer protocol
487
- e.g. ` list ` , ` array ` or ` bytearray ` .
546
+ e.g. ` list ` , ` array ` or ` bytearray ` .
488
547
489
548
Attributes of ` RingbufQueue ` :
490
549
1 . It is of fixed size, ` Queue ` can grow to arbitrary size.
@@ -515,7 +574,7 @@ Asynchronous methods:
515
574
block until space is available.
516
575
* ` get ` Return an object from the queue. If empty, block until an item is
517
576
available.
518
-
577
+
519
578
Retrieving items from the queue:
520
579
521
580
The ` RingbufQueue ` is an asynchronous iterator. Results are retrieved using
@@ -539,28 +598,27 @@ def add_item(q, data):
539
598
except IndexError :
540
599
pass
541
600
```
542
-
543
601
###### [ Contents] ( ./EVENTS.md#0-contents )
544
602
545
603
# 100 Appendix 1 Polling
546
604
547
605
The primitives or drivers referenced here do not use polling with the following
548
606
exceptions:
549
607
1 . Switch and pushbutton drivers. These poll the ` Pin ` instance for electrical
550
- reasons described below.
608
+ reasons described below.
551
609
2 . ` ThreadSafeFlag ` and subclass ` Message ` : these use the stream mechanism.
552
610
553
611
Other drivers and primitives are designed such that paused tasks are waiting on
554
612
queues and are therefore using no CPU cycles.
555
613
556
614
[ This reference] [ 1e ] states that bouncing contacts can assume invalid logic
557
- levels for a period. It is a reaonable assumption that ` Pin.value() ` always
615
+ levels for a period. It is a reasonable assumption that ` Pin.value() ` always
558
616
returns 0 or 1: the drivers are designed to cope with any sequence of such
559
617
readings. By contrast, the behaviour of IRQ's under such conditions may be
560
618
abnormal. It would be hard to prove that IRQ's could never be missed, across
561
619
all platforms and input conditions.
562
620
563
- Pin polling aims to use minimal resources, the main overhead being ` uasyncio ` 's
621
+ Pin polling aims to use minimal resources, the main overhead being ` asyncio ` 's
564
622
task switching overhead: typically about 250 μs. The default polling interval
565
623
is 50 ms giving an overhead of ~ 0.5%.
566
624
0 commit comments