I2C LCD: code inside


  • Pybytes Beta

    Very interested by your code version, thanks in advance.



  • This post is deleted!


  • Got the 128x64 display this week. Stupid chinese seller sent SPI version instead of I2C.
    But I was able to add SPI support as well :)
    alt text

    Will update code soon.



  • @brotherdust Thank you very much for quick reply. :o)



  • @rskoniec said in I2C LCD: code inside:

    @brotherdust Any chance for a code and answers for @DVE questions?

    @rskoniec, my apologies. I thought that I had answered this and didn't give it any more thought.

    The commit I referenced enables the I2C primitives. You'll need to recompile the firmware and upload it. Then the code will work.

    After reviewing the code a bit more, I think it can be refactored not to use the primitives (I2C.start, stop, etc). The primitives are already a part of other, more high-level routines. But the easiest way to get the driver going was just to enable those functions. =) I'm too lazy to re-factor and go through all the pull requests and justifications to get it included upstream.



  • @brotherdust Any chance for a code and answers for @DVE questions?



  • @brotherdust Its great :)

    I ordered 128x64 display too on Ebay, will be able to test next week.

    @brotherdust said in I2C LCD: code inside:

    The driver was using I2C "primitives" that were disabled in the esp32 version of machine_i2c

    How it works? Do you need to reload a firmware with enabled primitives?



  • It works! Yay! Code later. Sleep now.



  • @DVE Update on using the built-in framebuffer and SSD1306 driver:
    The driver was using I2C "primitives" that were disabled in the esp32 version of machine_i2c. The recent commits I've made on my relevant github repo:
    https://github.com/brotherdust/pycom-micropython/commits/i2c-framebuf

    I've made progress; but still learning how to use this stuff!



  • Here's what I'm putting in:

    # MicroPython SSD1306 OLED driver, I2C and SPI interfaces
    
    from micropython import const
    import time
    import framebuf
    
    
    # register definitions
    SET_CONTRAST        = const(0x81)
    SET_ENTIRE_ON       = const(0xa4)
    SET_NORM_INV        = const(0xa6)
    SET_DISP            = const(0xae)
    SET_MEM_ADDR        = const(0x20)
    SET_COL_ADDR        = const(0x21)
    SET_PAGE_ADDR       = const(0x22)
    SET_DISP_START_LINE = const(0x40)
    SET_SEG_REMAP       = const(0xa0)
    SET_MUX_RATIO       = const(0xa8)
    SET_COM_OUT_DIR     = const(0xc0)
    SET_DISP_OFFSET     = const(0xd3)
    SET_COM_PIN_CFG     = const(0xda)
    SET_DISP_CLK_DIV    = const(0xd5)
    SET_PRECHARGE       = const(0xd9)
    SET_VCOM_DESEL      = const(0xdb)
    SET_CHARGE_PUMP     = const(0x8d)
    
    
    class SSD1306:
        def __init__(self, width, height, external_vcc):
            self.width = width
            self.height = height
            self.external_vcc = external_vcc
            self.pages = self.height // 8
            self.buffer = bytearray(self.pages * self.width)
            self.framebuf = framebuf.FrameBuffer1(self.buffer, self.width, self.height)
            self.poweron()
            self.init_display()
    
        def init_display(self):
            for cmd in (
                SET_DISP | 0x00, # off
                # address setting
                SET_MEM_ADDR, 0x00, # horizontal
                # resolution and layout
                SET_DISP_START_LINE | 0x00,
                SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
                SET_MUX_RATIO, self.height - 1,
                SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
                SET_DISP_OFFSET, 0x00,
                SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12,
                # timing and driving scheme
                SET_DISP_CLK_DIV, 0x80,
                SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
                SET_VCOM_DESEL, 0x30, # 0.83*Vcc
                # display
                SET_CONTRAST, 0xff, # maximum
                SET_ENTIRE_ON, # output follows RAM contents
                SET_NORM_INV, # not inverted
                # charge pump
                SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,
                SET_DISP | 0x01): # on
                self.write_cmd(cmd)
            self.fill(0)
            self.show()
    
        def poweroff(self):
            self.write_cmd(SET_DISP | 0x00)
    
        def contrast(self, contrast):
            self.write_cmd(SET_CONTRAST)
            self.write_cmd(contrast)
    
        def invert(self, invert):
            self.write_cmd(SET_NORM_INV | (invert & 1))
    
        def show(self):
            x0 = 0
            x1 = self.width - 1
            if self.width == 64:
                # displays with width of 64 pixels are shifted by 32
                x0 += 32
                x1 += 32
            self.write_cmd(SET_COL_ADDR)
            self.write_cmd(x0)
            self.write_cmd(x1)
            self.write_cmd(SET_PAGE_ADDR)
            self.write_cmd(0)
            self.write_cmd(self.pages - 1)
            self.write_data(self.buffer)
    
        def fill(self, col):
            self.framebuf.fill(col)
    
        def pixel(self, x, y, col):
            self.framebuf.pixel(x, y, col)
    
        def scroll(self, dx, dy):
            self.framebuf.scroll(dx, dy)
    
        def text(self, string, x, y, col=1):
            self.framebuf.text(string, x, y, col)
    
    
    class SSD1306_I2C(SSD1306):
        def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False):
            self.i2c = i2c
            self.addr = addr
            self.temp = bytearray(2)
            super().__init__(width, height, external_vcc)
    
        def write_cmd(self, cmd):
            self.temp[0] = 0x80 # Co=1, D/C#=0
            self.temp[1] = cmd
            self.i2c.writeto(self.addr, self.temp)
    
        def write_data(self, buf):
            self.temp[0] = self.addr << 1
            self.temp[1] = 0x40 # Co=0, D/C#=1
            self.i2c.start()
            self.i2c.write(self.temp)
            self.i2c.write(buf)
            self.i2c.stop()
    
        def poweron(self):
            pass
    
    from machine import I2C
    i2c = I2C(0, I2C.MASTER, baudrate=1000000)
    display = SSD1306_I2C(width=128, height=64, i2c=i2c, addr=0x3d)
    

    The 128x64 display has hardware address of 0x3d. Anyway, here's what comes out:

    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 109, in __init__
      File "<stdin>", line 37, in __init__
      File "<stdin>", line 64, in init_display
      File "<stdin>", line 89, in show
      File "<stdin>", line 119, in write_data
    AttributeError: 'I2C' object has no attribute 'start'
    

    The display turns on, but it's full of what looks like random noise. I looked through the source code for the I2C module and the start method is there:
    https://github.com/pycom/pycom-micropython/blob/master/esp32/mods/machine_i2c.c#L348

    STATIC mp_obj_t machine_i2c_start(mp_obj_t self_in) {
        machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
        mp_hal_i2c_start(self);
        return mp_const_none;
    }
    MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_start_obj, machine_i2c_start);
    
    >>> help(machine.I2C)
    <class 'I2C'>object  is of type type
      init -- <function>
      scan -- <function>
      readfrom -- <function>
      readfrom_into -- <function>
      writeto -- <function>
      readfrom_mem -- <function>
      readfrom_mem_into -- <function>
      writeto_mem -- <function>
      MASTER -- 0
    

    I don't get why it's not present in the list of available methods for this class. =/



  • @rskoniec said in I2C LCD: code inside:

    @brotherdust Waiting for the news. :o)

    Sorry! I was hacking on NyQuil last night. I got the framebuf code to compile and loaded it up on to one of my LoPy's. Tried the driver code, but all I got was the same result: a screen full of static. Same result as when I used @DVE 's code. Maybe my module is defective? Not sure. Anyway, tried a few things, couldn't get it to do anything different, then rage-quit because I wasn't clear-headed enough to figure out what the LoPy was telling me on the console. heh. Either of you have any luck?



  • @brotherdust, thanks for the link. I can test this driver also.



  • @brotherdust Waiting for the news. :o)



  • @DVE So, I tried your code on my 128x64. It just shows up as a bunch of random noise on the display. =( I dug through the MP codebase and found this: https://github.com/pycom/pycom-micropython/blob/master/drivers/display/ssd1306.py

    Lo and behold! It's for the same display driver as what we have! Wait.. no framebuf module.

    Twiddled with the make configurations and managed to make a build with framebuf included. Trying it now. It's late so I might not report back until tomorrow.



  • @brotherdust said in I2C LCD: code inside:

    @DVE said in I2C LCD: code inside:

    Strings support added:
    alt text

    Usage is easy, example:

        import sys,  machine
        
        initialize()
        set_contrast(128) # 1-255
        displayOn()
        clearBuffer()
        addString(0, 0,  sys.platform + " " + sys.version)
        addString(0, 1,  "---")
        addString(0, 2,  "CPU: {} MHz".format(machine.freq()[0]/1000000))
        drawBuffer()
    

    @DVE Thanks for the code! I ordered a 128x96 display from Adafruit last week and expect to get it on Wednesday. When I have it in-hand, I will test your code and send a pull request over if I can get working. =)

    ahem Sorry. 128x64.



  • @DVE said in I2C LCD: code inside:

    Strings support added:
    alt text

    Usage is easy, example:

        import sys,  machine
        
        initialize()
        set_contrast(128) # 1-255
        displayOn()
        clearBuffer()
        addString(0, 0,  sys.platform + " " + sys.version)
        addString(0, 1,  "---")
        addString(0, 2,  "CPU: {} MHz".format(machine.freq()[0]/1000000))
        drawBuffer()
    

    @DVE Thanks for the code! I ordered a 128x96 display from Adafruit last week and expect to get it on Wednesday. When I have it in-hand, I will test your code and send a pull request over if I can get working. =)



  • @DVE OK, thanks. I've just ordered one so I'll have the chance to check and let you know.



  • 128x64 display support was added to the source code, but I don't have a real display to test. So, you can try, but no guarantee :)



  • @DVE Great! What about 128x64 version, because I saw some references in the code? Is it working?



  • @DVE Awesome!, thanks for sharing :-)


 

Pycom on Twitter