rm-hull / luma.oled

Python module to drive a SSD1306 / SSD1309 / SSD1322 / SSD1325 / SSD1327 / SSD1331 / SSD1351 / SH1106 OLED
https://luma-oled.readthedocs.io
MIT License
805 stars 161 forks source link

SSD1305 controller with 128x32 display issue. #309

Closed Trotter73 closed 1 year ago

Trotter73 commented 3 years ago

Hi,

Firstly I appreciate that I am unsupported here but thought I would raise an issue in case there is a wider problem..

I recently purchased a Waveshare 2.23” OLED HAT for a Pi Zero project I was working on, the display is 128x32 and uses the SSD1305 controller, link to the device… https://www.waveshare.com/wiki/2.23inch_OLED_HAT

Initially I used the drivers supplied by Waveshare to check the display and prove my code, no issues with them, and I got code that worked, the support though is not fantastic…

On reading about the SSD1305 controller I found that it was supposed to be code compatible with the SSD1306 and SSD1309, with some exceptions around the charge pump, so I thought I would give Luma-OLED a try..

After installing, I can run the example code and get an output on the display, however there appears to be an issue with the height or scaling, there are some random pixels to the right of the screen and it looks as if every other horizontal line is being skipped, I have attached a picture. Searching through the issues here this looks like it was previously a problem with 128x32 devices and it looks as if it was fixed at some point in 2017, I wasn’t sure if this is a particular issue with the SSD1305 or whether the issue with 128x32 devices has reappeared.

If it is a controller incompatibility issue, I would be more than happy to work with you guys on adding this one, I am no coder but could look at areas if pointed in the right direction.

The project is running on a Raspberry Pi Zero W, Buster with kernel “Linux raspberrypi 5.4.51+ #1333”

Many Thanks, M.

python3 demo.py -d ssd1306 -i spi --width 128 --height 32 Version: luma.oled 3.7.0 (luma.core 1.17.3) Display: ssd1306 Interface: spi Dimensions: 128 x 32 20201029_083711

20201015_133219

mark-zarandi commented 3 years ago

Im having this same issue on sh1106 where it used to work perfectly.

mark-zarandi commented 3 years ago

@Trotter73 Hi Mr. Trotter - Reflashed rasbian to see if that fixed it. I'm looking around, and thinking about what my screen mighta been through and i think this is a symptom of a broken screen.

rm-hull commented 3 years ago

This looks to be a memory mapping issue. The GDRAM inside the device is layer out in pages that interleaved, and if the init sequence is not correct then this could result in the image @Trotter73 shows.

Looking back through the git history, the code for SSD1306 hasn’t changed in a few years. But what has changed is the performance of the raspberry pi. It could be that the init sequence is being supplied too quickly for the display to consume it, and so it doesn’t get initialized properly. We may need to add a delay at some relevant points. This is complete conjecture by the way..

Anyway, I will try and find my 128x32 oled and give it a try, but in the meantime, could you try adding --spi-bus-speed=500000 when you run the demo (this is the lowest bus speed it supports) to see if indeed it might be a timing issue?

Trotter73 commented 3 years ago

Hi,

Thanks for taking the time to reply, appreciated !

As suggested I appended --spi-bus-speed=500000 but it gave the same result, out of curiosity I also under-clocked my Zero to Pi B speeds to see if that made a difference, but again no change, the display was not as expected.

arm_freq=700 core_freq=250 sdram_freq=400

For reference the same SD card put in a Zero with a SH1106 controller and 128x64 display works as expected.

If you are in the UK I could pop this display in the post if you think that would help ?

M.

rm-hull commented 3 years ago

I think I might have a 128x32 device somewhere, but if not I might take you up on that (I am in the UK) let me know what your email is (ping me at rm_hull@yahoo.co.uk) and I’ll give you the mailing address

Trotter73 commented 3 years ago

Hi,

Just sent you an email.

mark-zarandi commented 3 years ago

Thanks for looking at it ya'll. This project has brought me a lot of joy. I'll follow-up with ya'll on sh1106 once i finish my own testing.

rm-hull commented 3 years ago

So, I dug out my 128x32 SSD1306 (which incidentally is I2C rather than SPI) and ran the following:

$ python3 examples/demo.py -d ssd1306  --i2c-port=1 --width=128 --height=32

and it lit up as follows:

image

I dont think I2C vs SPI is making any difference here, but it does suggest that your SSD1305 either:

Looking at the differences between the different screen resolutions, the multiplexing, display clock divider and COM pins settings are the only differences (see https://github.com/rm-hull/luma.oled/blob/master/luma/oled/device/__init__.py#L158-L164), it seems like if we "tweak" those values we may arrive at the correct settings for your device.

Looking at the code sample they provide (https://www.waveshare.com/w/upload/c/c5/2.23inch-OLED-HAT-Code.7z) in the SSD1305.py file the following values are used, the ones that I think are relevant i've marked as PERTINENT:

    def _initialize(self):
        # 128x32 pixel specific initialization.
        self.command(0xAE)#--turn off oled panel
        self.command(0x04)#--turn off oled panel    
        self.command(0x10)#--turn off oled panel
        self.command(0x40)#---set low column address
        self.command(0x81)#---set high column address
        self.command(0x80)#--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
        self.command(0xA1)#--set contrast control register
        self.command(0xA6)# Set SEG Output Current Brightness

        # PERTINENT 
        self.command(0xA8)#--Set SEG/Column Mapping     0xa0×óÓÒ·´ÖÃ 0xa1Õý³£ 
        self.command(0x1F)#Set COM/Row Scan Direction   0xc0ÉÏÏ·´Öà 0xc8Õý³£

        self.command(0xC8)#--set normal display
        self.command(0xD3)#--set multiplex ratio(1 to 64)
        self.command(0x00)#--1/64 duty
        self.command(0xD5)#-set display offset  Shift Mapping RAM Counter (0x00~0x3F)
        self.command(0xF0)#-not offset

        # PERTINENT 
        self.command(0xd8)#--set display clock divide ratio/oscillator frequency
        self.command(0x05)#--set divide ratio, Set Clock as 100 Frames/Sec

        self.command(0xD9)#--set pre-charge period
        self.command(0xC2)#Set Pre-Charge as 15 Clocks & Discharge as 1 Clock

        # PERTINENT 
        self.command(0xDA)#--set com pins hardware configuration
        self.command(0x12)
        self.command(0xDB)#--set vcomh
        self.command(0x08)#Set VCOM Deselect Level
        self.command(0xAF)#-Set Page Addressing Mode (0x00/0x01/0x02)

The only one of these that I think is likely to affect your display is the COM pins setting - looking at the data sheet (https://www.waveshare.com/w/upload/b/b5/SSD1305-Revision_1.8.pdf) on pg 51 section heading "10.1.26 Set COM Pins Hardware Configuration (DAh)" this enumerates the acceptable values...

If you modified the demo.py file https://github.com/rm-hull/luma.examples/blob/master/examples/demo.py#L53-L56 to look like this:

def main():
    device = get_device()
    device.command(0xDA, 0x12)        # <--- insert this line

    print("Testing basic canvas graphics...")

If that doesn't work, try progressively addingdevice.command(0xD8, 0x05) and device.command(0xA8, 0x1F).

Pictures would help to diagnose further. Else the last resort is you can loan me your device. Ultimately we would probably need to create a new class for ssd1305 with its own distinct init sequence

Trotter73 commented 3 years ago

Hi,

You were correct 0xDA, 0x12 made huge improvements on the look of the display, we are almost there.

In addition to these 0xDB & 0x08 were needed to make the contrast section of the demo work.

The rest of the options didn't appear to make any difference, however this was only a really quick check.

However, there is still the issue of the stray pixels on the right hand side of the display, examples attached...

Me. 20201111_100846 20201111_100826

rm-hull commented 3 years ago

@Trotter73 try adding 0xD5, 0xF0 to your custom init command, eg:

def main():
    device = get_device()
    device.command(0xDA, 0x12, 0xD5, 0xF0)        # <--- insert this line

    print("Testing basic canvas graphics...")
Trotter73 commented 3 years ago

@rm-hull No change, still the shift to the left and odd pixels on the right, as per the picture above...

Trotter73 commented 3 years ago

Not sure if it matters but the description of these differs to the documentation for the ssd1305..

self.command(0xD3)#--set multiplex ratio(1 to 64)
self.command(0x00)#--1/64 duty
self.command(0xD5)#-set display offset  Shift Mapping RAM Counter (0x00~0x3F)
self.command(0xF0)#-not offset
rm-hull commented 3 years ago

Maybe take all the values in the waveshareinitialize() method and transcribe them into a list and supply them to display.command(...)

If that works, we can just create an ssd1305 class that has a common base with ssd1306 but just has a different init sequence.

Trotter73 commented 3 years ago

Ok cool, I think I know what I'm doing there, was going to give that a go anyway. Will try to have a look at that tomorrow.

In the Waveshare main.py there are some other values, constants I think, does anything need to be done with them?

Trotter73 commented 3 years ago

@rm-hull OK so I thought I knew what you were suggesting but I was wrong lol. If you could give a little example or pointer I'll collate the list and try....

rm-hull commented 3 years ago

hi @Trotter73, sorry I missed your response (for some reason some but not all github notifications keep getting snagged in my spam folder)

I meant: could you add the following:

display.command(
    0xAE, 0x04, 0x10, 0x40, 0x81, 0x80, 0xA1, 0xA6, 
    0xA8, 0x1F, 0xC8, 0xD3, 0x00, 0xD5, 0xF0, 0xd8, 
    0x05, 0xD9, 0xC2, 0xDA, 0x12, 0xDB, 0x08, 0xAF)

(This is the complete init sequence from the SSD1305.py file)

Trotter73 commented 3 years ago

Hi,

@rm-hull

No worries, funnily enough I have the same here, all git email seems to be dumped to spam...

OK, so adding the above gives the the following, basically the random pixels om the right are now two bars...

20201123_150931

I was also looking at the constants and comparing those and found some differences

% = Present in SSD1305.py but not const.py

=Values are the same in SSD1305.py and const.py

? = I think there is a different value

`# Constants %SSD1305_I2C_ADDRESS = 0x3C # 011110+SA0+RW - 0x3C or 0x3D %SSD1305_SETCONTRAST = 0x81 %SSD1305_DISPLAYALLON_RESUME = 0xA4 %SSD1305_DISPLAYALLON = 0xA5 %SSD1305_NORMALDISPLAY = 0xA6 %SSD1305_INVERTDISPLAY = 0xA7 %SSD1305_DISPLAYOFF = 0xAE %SSD1305_DISPLAYON = 0xAF

SSD1305_SETDISPLAYOFFSET = 0xD3

SSD1305_SETCOMPINS = 0xDA

SSD1305_SETVCOMDETECT = 0xDB

SSD1305_SETDISPLAYCLOCKDIV = 0xD5

SSD1305_SETPRECHARGE = 0xD9

%SSD1305_SETMULTIPLEX = 0xA8

SSD1305_SETLOWCOLUMN = 0x00

SSD1305_SETHIGHCOLUMN = 0x10

SSD1305_SETSTARTLINE = 0x40

SSD1305_MEMORYMODE = 0x20

SSD1305_COLUMNADDR = 0x21

SSD1305_PAGEADDR = 0x22

SSD1305_COMSCANINC = 0xC0

SSD1305_COMSCANDEC = 0xC8

?SSD1305_SEGREMAP = 0xA0

SSD1305_CHARGEPUMP = 0x8D

SSD1305_EXTERNALVCC = 0x1

SSD1305_SWITCHCAPVCC = 0x2

Scrolling constants

%SSD1305_ACTIVATE_SCROLL = 0x2F %SSD1305_DEACTIVATE_SCROLL = 0x2E %SSD1305_SET_VERTICAL_SCROLL_AREA = 0xA3 %SSD1305_RIGHT_HORIZONTAL_SCROLL = 0x26 %SSD1305_LEFT_HORIZONTAL_SCROLL = 0x27 %SSD1305_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29 %SSD1305_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A`

So I changed /usr/local/lib/python3.7/dist-packages/luma/oled/const.py to make SETSEGMENTREMAP = 0xA0 instead of 0xA1 and the random pixels appear to have gone, however if I change it back to 0xA1 they don't reappear ?! The screen is still offset though..

Is there a method of passing constants to demo.py, similar to that of the commands ? Edit:- Adding the extra ones into the const.py file does not appear to work, although I didn't really expect it to

20201123_160211

Trotter73 commented 3 years ago

I'm going to be really rude and bump this, any ideas on how we can get this over the line, we seems to be all but there ...

rm-hull commented 3 years ago

sorry, quite right (and not rude) to bump. Could you try adding:

def main():
    device = get_device()
    device.command(...)        

    device._colstart += 1    # <--- insert these lines
    device._colend += 1  

    print("Testing basic canvas graphics...")

and let me know if that makes any difference?

Trotter73 commented 3 years ago

Hi,

Thanks for the understanding !

So I had to start with a fresh install, this is what I have in demo.py

def main(): device = get_device() device.command( 0xAE, 0x04, 0x10, 0x40, 0x81, 0x80, 0xA1, 0xA6, 0xA8, 0x1F, 0xC8, 0xD3, 0x00, 0xD5, 0xF0, 0xd8, 0x05, 0xD9, 0xC2, 0xDA, 0x12, 0xDB, 0x08, 0xAF) device._colstart += 1 device._colend += 1

The additional couple of lines didn't make any difference, the output is as per the last pic in this thread, still shifted to the left a bit..

Regards,

Trotter73 commented 3 years ago

However..... Just had a play and the following values appear to fix the issue... I tried values either side but 4 was the sweet spot...

device._colstart += 4
device._colend += 4

20201209_134936 20201209_134953

So to wrap up adding the following to demo.py gives the correct output

device.command(
    0xAE, 0x04, 0x10, 0x40, 0x81, 0x80, 0xA1, 0xA6,
    0xA8, 0x1F, 0xC8, 0xD3, 0x00, 0xD5, 0xF0, 0xd8,
    0x05, 0xD9, 0xC2, 0xDA, 0x12, 0xDB, 0x08, 0xAF)
device._colstart += 4
device._colend += 4
rm-hull commented 3 years ago

@Trotter73 ok, thanks for that - good news .. it should be enough for me to be able to create a specific SSD1305 device with this init wrapped inside it, but in the meantime until this is published, you are ok to proceed with the above as an interrim hack?

Trotter73 commented 3 years ago

@rm-hull Thanks for all your help in getting this working, it really is appreciated, I'm more than happy with the interim solution, I've already tried it with some other code I had written for a SH1106 and it works a treat. Just shout if you want anything testing, you have my email, if you don't get a reply here ping me there.

Thanks again.

ldritsas commented 3 years ago

This issue is a few months old, I know, but as another Pi user and owner of the same Waveshare 2.23" 128x32 OLED, the eventual appearance of specific SSD1305 device from luma.oled would be great! I am learning a lot creating my own code for it, but using luma would be so much better. I prefer SPI, and the only properly documented library at the moment would requite getting the soldering iron out to force it into I2C mode (then there is an Adafruit CircuitPython SSD1305 library that would allow drawing on the display with PIL but it does not support SPI). All other SSD1305 code is very Arduino-specific.

kintel commented 2 years ago

I'm using the "Adafruit 2.23" Monochrome OLED Bonnet for Raspberry Pi" which is another, very similar, SSD1305 display (https://www.adafruit.com/product/4567). Looking at the datasheet differences and the command list mentioned above, I reduced the changes needed for this display to:

device = ssd1306(width=128, height=32,  ...)
device.command(0xDA, 0x12) # Use alternate COM pin configuration
device._colstart += 4
device._colend += 4
Trotter73 commented 1 year ago

@Trotter73 ok, thanks for that - good news .. it should be enough for me to be able to create a specific SSD1305 device with this init wrapped inside it, but in the meantime until this is published, you are ok to proceed with the above as an interrim hack?

Hi, not sure if this is still actively in development? Would there be a lot of work to add the SSD1305 device? Or if someone could give some pointers on how/where to start I could have a go at creating a device and generating a pull request ....

vieacq commented 1 year ago

I'd suggest to duplicate class ssd1306 and modify appropriately to get class ssd1305 in __init__.py. I'd guess modifications should go before/into/after self.command (lines around 260 of __init__.py) Maybe this is "brute force" (as it does not subclass from class ssd1306), but probably works... Unfortunately I don't (yet) have the SSD1305 (but want to get this 2+" OLED for my planned project) - so cannot try atm.

vieacq commented 1 year ago

...in oled/device/__init__.py - and also add "ssd1305" to __all__ (line 48)

olly69 commented 7 months ago

Thanks @Trotter73 and @rm-hull for saving me a lot of time! Very useful.