Please support memoryview and / or expose low level I2C start and stop


#1

I’m developing an SSD1306 I2C driver and when sending data over the bus to the device the sequence to copy directly to the device Ram starts with 0x40 and end with an I2C stop.

So for example in micropython one would have the following code snippet:

self.i2c.start()
self.i2c.write(0x40)
self.i2c.write(buf)
self.i2c.stop()

This does not work in Zerynth because the I2C write() seems to initiate an I2C start then send the data and stop.

self.i2c.write(0x40)
self.i2c.write(buf)

Results in no data being recognised as there is an I2C bus stop after sending 0x40 signalling data transmission has ended.

Without being able to control the low level I2C bus state then a simple way would be to have code like the following:

screen_buffer_length = width//8 x height # Assume 8 bits per byte
wbuf=bytearray(1+screen_buffer_length) # Data write buffer
sbuf=memoryview(wbuf)[1:screen_buffer_length) # Screen buffer

# Do stuff in the buffer like pixels, text

self.write(wbuf) # Performs I2C start, sends 0x40 then the screen buffer data, I2C stop

Alas memoryview causes a compile error with:

Can’t find name [memoryview] in …

So one has to have two buffers and just before sending perform:

wbuf[1:screen_buffer_length+1] = sbuf

Or the code has to offset by 1 byte every time it manipulates the buffer which adds complexity / slows down the code i.e. function call.

A more elegant solution would be to implement memoryview and or make I2C stop and start controllable in Python.

Thoughts?


#2

Hi @IainColledge,

great that your are developing the SSD1306 I2C support! Eventually you could release the driver as a community library.

Concerning your questions:

  • exposing lower level I2C functionalities is feasible, but would require a great effort in adapting all developed I2C drivers, moreover we prefer to keep the simple write/read interface;

  • a memoryview module could, on the other hand, be developed, probably even outside the VM, exploiting Zerynth hybrid C/Python programming capabilities. In the meantime, you could try to simulate it, with a simple Python class shifting the start of your bytearray if that’s the only memoryview feature you need.

     import streams
    
     class memoryview():
    
         def __init__(self, buf, start_index, end_index):
             self._buf = buf
             self._start_i = start_index
             self._end_i = end_index
    
         def __getitem__(self, key):
             if type(key) == PSLICE:
                 return memoryview(self._buf, self._start_i + key[0], self._start_i + key[1])
             elif type(key) == PSMALLINT:
                 return self._buf[self._start_i + key]
             raise IndexError
    
         def __setitem__(self, key, value):
             if type(key) == PSLICE:
                 self._buf[self._start_i + key[0]:min(self._start_i + key[1], self._end_i)] = value
             elif type(key) == PSMALLINT:
                 self._buf[self._start_i + key] = value
             else:
                 raise IndexError
    
    
     streams.serial()
    
     mybuf = bytearray(10)
     mybuf[0] = 0x40
     sbuf = memoryview(mybuf, 1, len(mybuf)-1)
     sbuf[0] = 0x32
     sbuf[2] = 0x33
    
     ssbuf = sbuf[3:5]
     ssbuf[0] = 0x77
    
     print('-'.join(['%02x' % byte for byte in mybuf]))

#3

Thanks very much, that looks good indeed and seems to do away with the excessive memory utilisation and or creating / destroying objects, which is better.

Hopefully will release as a community library but this is my first week in Zerynth :wink:

I understand about the work breaking out stop & start on the bus however it is a common control handle on the bus, certainly something to point out in the documentation and maybe works arounds a discussed here.

Memoryview would seem to be something good to have in a MCU environment like Zerynth so hopefully will be considered for implementation some point in the future.