Skip to content

Commit 7161dad

Browse files
committed
as_i2c_i.py Add optional coro args.
1 parent 07aae17 commit 7161dad

File tree

2 files changed

+75
-22
lines changed

2 files changed

+75
-22
lines changed

i2c/README.md

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,20 @@ configured for a transfer before the master attempts to access it.
1515

1616
The Pyboard or similar STM based boards are currently the only targets
1717
supporting I2C slave mode. Consequently at least one end of the interface
18-
(known as the`Initiator`) must be a Pyboard. The other end may be any hardware
19-
running MicroPython.
18+
(known as the`Initiator`) must be a Pyboard or other board supporting the `pyb`
19+
module. The `Responder` may be any hardware running MicroPython and supporting
20+
`machine`.
2021

21-
`Initiator` user applications may implement a timeout to enable detection of
22-
failure of the other end of the interface (the `Responder`). The `Initiator`
23-
can reset the `Responder` in this event.
22+
If the `Responder` (typically an ESP8266) crashes the resultant I2C failure is
23+
detected by the `Initiator` which can issue a hardware reboot to the
24+
`Responder` enabling the link to recover. This can occur transparently to the
25+
application and is covered in detail
26+
[in section 5.3](./README.md#53-responder-crash-detection).
2427

2528
## Changes
2629

27-
v0.16 Minor improvements and bugfixes. Eliminate `timeout` option which caused
30+
V0.17 Dec 2018 Initiator: add optional "go" and "fail" user coroutines.
31+
V0.16 Minor improvements and bugfixes. Eliminate `timeout` option which caused
2832
failures where `Responder` was a Pyboard.
2933
V0.15 RAM allocation reduced. Flow control implemented.
3034
V0.1 Initial release.
@@ -40,6 +44,7 @@ V0.1 Initial release.
4044
4.1 [Channel class](./README.md#41-channel-class)
4145
4.2 [Initiator class](./README.md#42-initiator-class)
4246
4.2.1 [Configuration](./README.md#421-configuration) Fine-tuning the interface.
47+
4.2.2 [Optional coroutines](./README.md#422-optional-coroutines)
4348
4.3 [Responder class](./README.md#43-responder-class)
4449
5. [Limitations](./README.md#5-limitations)
4550
5.1 [Blocking](./README.md#51-blocking)
@@ -109,7 +114,7 @@ this interface.
109114

110115
A further issue common to most communications protocols is synchronisation:
111116
the devices won't boot simultaneously. Initially, and after the `Initiator`
112-
reboots the `Responder`, both ends run a synchronisation phase. The iterface
117+
reboots the `Responder`, both ends run a synchronisation phase. The interface
113118
starts to run once each end has determined that its counterpart is ready.
114119

115120
The design assumes exclusive use of the I2C interface. Hard or soft I2C may be
@@ -219,12 +224,17 @@ Coroutine:
219224

220225
## 4.2 Initiator class
221226

222-
Constructor args:
227+
##### Constructor args:
223228
1. `i2c` An `I2C` instance.
224229
2. `pin` A `Pin` instance for the `sync` signal.
225230
3. `pinack` A `Pin` instance for the `ack` signal.
226231
4. `reset=None` Optional tuple defining a reset pin (see below).
227232
5. `verbose=True` If `True` causes debug messages to be output.
233+
6. `cr_go=False` Optional coroutine to run at startup. See
234+
[4.2.2](./README.md#422-optional-coroutines).
235+
7. `go_args=()` Optional tuple of args for above coro.
236+
8. `cr_fail=False` Optional coro to run on ESP8266 fail or reboot.
237+
9. `f_args=()` Optional tuple of args for above.
228238

229239
The `reset` tuple consists of (`pin`, `level`, `time`). If provided, and the
230240
`Responder` times out, `pin` will be set to `level` for duration `time` ms. A
@@ -239,26 +249,26 @@ If the `Initiator` has no `reset` tuple and the `Responder` times out, an
239249

240250
`Pin` instances passed to the constructor must be instantiated by `machine`.
241251

242-
Class variables:
252+
##### Class variables:
243253
1. `t_poll=100` Interval (ms) for `Initiator` polling `Responder`.
244254
2. `rxbufsize=200` Size of receive buffer. This should exceed the maximum
245255
message length.
246256

247-
Class variables should be set before instantiating `Initiator` or `Responder`.
248-
See [Section 4.4](./README.md#44-configuration).
257+
See [Section 4.2.1](./README.md#421-configuration).
249258

250-
Instance variables:
259+
##### Instance variables:
251260

252261
The `Initiator` maintains instance variables which may be used to measure its
253-
peformance. See [Section 4.4](./README.md#44-configuration).
262+
peformance. See [Section 4.2.1](./README.md#421-configuration).
254263

255-
Coroutine:
264+
##### Coroutine:
256265
1. `reboot` If a `reset` tuple was provided, reboot the `Responder`.
257266

258267
## 4.2.1 Configuration
259268

260269
The `Initiator` class variables determine the behaviour of the interface. Where
261-
these are altered, it should be done before instantiation.
270+
these are altered, it should be done before instantiating `Initiator` or
271+
`Responder`.
262272

263273
`Initiator.t_poll` This defines the polling interval for incoming data. Shorter
264274
values reduce the latency when the `Responder` sends data; at the cost of a
@@ -275,24 +285,55 @@ variables may be read:
275285

276286
See test program `i2c_init.py` for an example of using the above.
277287

288+
## 4.2.2 Optional coroutines
289+
290+
These are intended for applications where the `Responder` may reboot at runtime
291+
either because I2C failure was detected or because the application issues an
292+
explicit reboot command.
293+
294+
The `cr_go` and `cr_fail` coroutines provide for applications which implement
295+
an application-level initialisation sequence on first and subsequent boots of
296+
the `Responder`. Such applications need to ensure that the initialisation
297+
sequence does not conflict with other coros accessing the channel.
298+
299+
The `cr_go` coro runs after synchronisation has been achieved. It runs
300+
concurrently with the coro which keeps the link open (`Initiator._run()`), but
301+
should run to completion reasonably quickly. Typically it performs any app
302+
level synchronisation, starts or re-enables application coros, and quits.
303+
304+
The `cr_fail` routine will prevent the automatic reboot from occurring until
305+
it completes. This may be used to prevent user coros from accessing the channel
306+
until reboot is complete. This may be done by means of locks or task
307+
cancellation. Typically `cr_fail` will terminate when this is done, so that
308+
`cr_go` has unique access to the channel.
309+
310+
If an explicit `.reboot()` is issued, a reset tuple was provided, and `cr_fail`
311+
exists, it will run and the physical reboot will be postponed until it
312+
completes.
313+
314+
Typical usage:
315+
```python
316+
chan = asi2c_i.Initiator(i2c, syn, ack, rst, verbose, self._go, (), self._fail)
317+
```
318+
278319
###### [Contents](./README.md#contents)
279320

280321
## 4.3 Responder class
281322

282-
Constructor args:
323+
##### Constructor args:
283324
1. `i2c` An `I2C` instance.
284325
2. `pin` A `Pin` instance for the `sync` signal.
285326
3. `pinack` A `Pin` instance for the `ack` signal.
286327
4. `verbose=True` If `True` causes debug messages to be output.
287328

288329
`Pin` instances passed to the constructor must be instantiated by `machine`.
289330

290-
Class variables:
331+
##### Class variables:
291332
1. `addr=0x12` Address of I2C slave. This should be set before instantiating
292333
`Initiator` or `Responder`. If the default address (0x12) is to be overriden,
293334
`Initiator` application code must instantiate the I2C accordingly.
294-
2. `rxbufsize` Size of receive buffer. This should exceed the maximum message
295-
length. Consider reducing this in ESP8266 applications to save RAM.
335+
2. `rxbufsize=200` Size of receive buffer. This should exceed the maximum
336+
message length. Consider reducing this in ESP8266 applications to save RAM.
296337

297338
###### [Contents](./README.md#contents)
298339

i2c/asi2c_i.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,23 @@ class Initiator(Channel):
3737
t_poll = 100 # ms between Initiator polling Responder
3838
rxbufsize = 200
3939

40-
def __init__(self, i2c, pin, pinack, reset=None, verbose=True):
40+
def __init__(self, i2c, pin, pinack, reset=None, verbose=True,
41+
cr_go=False, go_args=(), cr_fail=False, f_args=()):
4142
super().__init__(i2c, pin, pinack, verbose, self.rxbufsize)
4243
self.reset = reset
44+
self.cr_go = cr_go
45+
self.go_args = go_args
46+
self.cr_fail = cr_fail
47+
self.f_args = f_args
4348
if reset is not None:
4449
reset[0].init(mode=machine.Pin.OUT, value = not(reset[1]))
4550
# Self measurement
4651
self.nboots = 0 # No. of reboots of Responder
4752
self.block_max = 0 # Blocking times: max
4853
self.block_sum = 0 # Total
4954
self.block_cnt = 0 # Count
50-
loop = asyncio.get_event_loop()
51-
loop.create_task(self._run())
55+
self.loop = asyncio.get_event_loop()
56+
self.loop.create_task(self._run())
5257

5358
def waitfor(self, val): # Wait for response for 1 sec
5459
tim = utime.ticks_ms()
@@ -57,7 +62,10 @@ def waitfor(self, val): # Wait for response for 1 sec
5762
raise OSError
5863

5964
async def reboot(self):
65+
self.close() # Leave own pin high
6066
if self.reset is not None:
67+
if self.cr_fail:
68+
await self.cr_fail(*self.f_args)
6169
rspin, rsval, rstim = self.reset
6270
self.verbose and print('Resetting target.')
6371
rspin(rsval) # Pulse reset line
@@ -72,6 +80,8 @@ async def _run(self):
7280
self.rxbyt = b''
7381
await self._sync()
7482
await asyncio.sleep(1) # Ensure Responder is ready
83+
if self.cr_go:
84+
self.loop.create_task(self.cr_go(*self.go_args)
7585
while True:
7686
gc.collect()
7787
try:
@@ -85,6 +95,8 @@ async def _run(self):
8595
self.block_cnt += 1
8696
self.block_sum += t
8797
self.nboots += 1
98+
if self.cr_fail:
99+
await self.cr_fail(*self.f_args)
88100
if self.reset is None: # No means of recovery
89101
raise OSError('Responder fail.')
90102

0 commit comments

Comments
 (0)