Skip to content

Commit d899710

Browse files
committed
Fails on ESP8266 after ~1000 passes.
1 parent b08304f commit d899710

File tree

2 files changed

+32
-10
lines changed

2 files changed

+32
-10
lines changed

i2c/asi2c.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
import utime
2828
from micropython import const, schedule
2929
import io
30+
import gc
31+
32+
import micropython
33+
micropython.alloc_emergency_exception_buf(100)
3034

3135
_MP_STREAM_POLL_RD = const(1)
3236
_MP_STREAM_POLL_WR = const(4)
@@ -36,6 +40,8 @@
3640
# between Initiator setting a pin and initiating an I2C transfer: ensure
3741
# Initiator sets up first.
3842
_DELAY = const(20) # μs
43+
# bytes objects are transmitted in blocks of size 16N where N is an integer
44+
upsize = lambda x : (x + 15) & ~15
3945

4046
# Base class provides user interface and send/receive object buffers
4147
class Channel(io.IOBase):
@@ -52,8 +58,12 @@ def __init__(self, i2c, own, rem, verbose, rxbufsize):
5258
self.txbyt = b'' # Data to send
5359
self.txsiz = bytearray(2) # Size of .txbyt encoded as 2 bytes
5460
self.rxbyt = b''
55-
self.rxbuf = bytearray(rxbufsize)
61+
self.rxbuf = bytearray(upsize(rxbufsize)) # Hold an integer no. of blocks
5662
self.rx_mv = memoryview(self.rxbuf)
63+
self.lstmv = []
64+
for n in range(16, len(self.rxbuf) + 16, 16): # Preallocate memoryviews
65+
self.lstmv.append(self.rx_mv[0 : n]) # for all data lengths
66+
self.nrx = 0 # No. of bytes received (ignoring padding)
5767
self.cantx = True # Remote can accept data
5868

5969
async def _sync(self):
@@ -72,7 +82,7 @@ def waitfor(self, val): # Initiator overrides
7282

7383
# Get incoming bytes instance from memoryview.
7484
def _handle_rxd(self, msg):
75-
self.rxbyt = bytes(msg)
85+
self.rxbyt = bytes(msg[:self.nrx])
7686

7787
def _txdone(self):
7888
self.txbyt = b''
@@ -122,7 +132,8 @@ def write(self, buf, off, sz):
122132
d = buf[off : off + sz]
123133
d = d.encode()
124134
l = len(d)
125-
self.txbyt = d
135+
# Pad to integer no. of blocks
136+
self.txbyt = b''.join((d, bytes(upsize(l) - l)))
126137
self.txsiz[0] = l & 0xff
127138
self.txsiz[1] = l >> 8
128139
return l
@@ -148,19 +159,27 @@ class Responder(Channel):
148159
rxbufsize = 200
149160
def __init__(self, i2c, pin, pinack, verbose=True):
150161
super().__init__(i2c, pinack, pin, verbose, self.rxbufsize)
162+
self._handle_rxd_ref = self._handle_rxd # Alocate RAM here
163+
self._re_enable_ref = self._re_enable
151164
loop = asyncio.get_event_loop()
152165
loop.create_task(self._run())
153166

154167
async def _run(self):
155168
await self._sync() # own pin ->0, wait for remote pin == 0
156-
self.rem.irq(handler = self._handler, trigger = machine.Pin.IRQ_RISING)
169+
self.rem.irq(handler = self._handler, trigger = machine.Pin.IRQ_RISING, hard = True)
170+
while True:
171+
await asyncio.sleep(1)
172+
gc.collect()
173+
174+
def _re_enable(self, _):
175+
self.rem.irq(handler = self._handler, trigger = machine.Pin.IRQ_RISING, hard = True)
157176

158177
# Request was received: immediately read payload size, then payload
159178
# On Pyboard blocks for 380μs to 1.2ms for small amounts of data
160179
def _handler(self, _, sn=bytearray(2), txnull=bytearray(2)):
161180
# tstart = utime.ticks_us() # TEST
162181
addr = Responder.addr
163-
self.rem.irq(handler = None, trigger = machine.Pin.IRQ_RISING)
182+
self.rem.irq(handler = None, trigger = machine.Pin.IRQ_RISING, hard = True)
164183
utime.sleep_us(_DELAY) # Ensure Initiator has set up to write.
165184
self.i2c.readfrom_into(addr, sn)
166185
self.own(1)
@@ -171,12 +190,13 @@ def _handler(self, _, sn=bytearray(2), txnull=bytearray(2)):
171190
if n:
172191
self.waitfor(1)
173192
utime.sleep_us(_DELAY)
174-
mv = memoryview(self.rx_mv[0 : n]) # allocates
193+
mv = self.lstmv[(n >> 4)]
175194
self.i2c.readfrom_into(addr, mv)
176195
self.own(1)
177196
self.waitfor(0)
178197
self.own(0)
179-
schedule(self._handle_rxd, mv) # Postpone allocation
198+
self.nrx = n
199+
schedule(self._handle_rxd_ref, mv) # Postpone allocation
180200

181201
self.own(1) # Request to send
182202
self.waitfor(1)
@@ -198,5 +218,5 @@ def _handler(self, _, sn=bytearray(2), txnull=bytearray(2)):
198218
self.own(0)
199219
self.waitfor(0)
200220
self._txdone() # Invalidate source
201-
self.rem.irq(handler = self._handler, trigger = machine.Pin.IRQ_RISING)
221+
schedule(self._re_enable_ref, 0)
202222
# print('Time: ', utime.ticks_diff(utime.ticks_us(), tstart))

i2c/asi2c_i.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import utime
2929
import gc
3030
from micropython import schedule
31-
from asi2c import Channel
31+
from asi2c import Channel, upsize
3232

3333
# The initiator is an I2C slave. It runs on a Pyboard. I2C uses pyb for slave
3434
# mode, but pins are instantiated using machine.
@@ -128,10 +128,12 @@ def _sendrx(self, rxbusy, sn=bytearray(2), txnull=bytearray(2)):
128128
if n:
129129
self.waitfor(1) # Wait for responder to request send
130130
self.own(1) # Acknowledge
131-
mv = memoryview(self.rx_mv[0 : n])
131+
mv = self.lstmv[(n >> 4)]
132+
# mv = memoryview(self.rx_mv[0 : upsize(n)])
132133
self.i2c.recv(mv, timeout=to)
133134
self.waitfor(0)
134135
self.own(0)
136+
self.nrx = n
135137
schedule(self._handle_rxd, mv) # Postpone allocation
136138
return True
137139
return False

0 commit comments

Comments
 (0)