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
812 stars 162 forks source link

Wrong Init Routine for SSD1322 ? / NHD-2.7-12864WD #199

Closed heavydetail closed 5 years ago

heavydetail commented 6 years ago

RPi3 B+ Linux 4.9.79-v7+ #1086 armv7l GNU/Linux

I got this new display recently for a project and have been trying to get started with it and luma.oled. Datasheet here: https://www.mouser.com/ds/2/291/NHD-2.7-12864WDY3-1116258.pdf It has an SSD1322 controller that I am trying to access via SPI on the raspi.

config is as such:

--display=ssd1322
--interface=spi
--spi-device=0
--spi-port=0
--gpio-reset=23
--gpio-data-command=24
--mode=RGB

Here is a video a first trial where it kind of worked, but now it doesn't react at all to any of the examples. It is clearly not initialized and driven in the right way. The rotating cube was the only example doing anything surprisingly for some moments, unclear why. https://www.youtube.com/watch?v=jjIOZmPjGdw

Here is the recommended init from the datasheet:

void NHD12864WDY3_Init(void){
  digitalWrite(RES, LOW); //pull /RES (pin #16) low
  delayUS(200);           //keep /RES low for minimum 200μs
  digitalWrite(RES, HIGH);//pull /RES high
  delayUS(200);           //wait minimum 200μs before sending commands
  writeCommand(0xAE);     //display OFF
  writeCommand(0xB3);     //set CLK div. & OSC freq.
    writeData(0x91);
  writeCommand(0xCA);     //set MUX ratio
    writeData(0x3F);
  writeCommand(0xA2);     //set offset
    writeData(0x00);
  writeCommand(0xAB);     //function selection
    writeData(0x01);
  writeCommand(0xA0);     //set re-map
    writeData(0x16);
    writeData(0x11);
  writeCommand(0xC7);     //master contrast current
    writeData(0x0F);
  writeCommand(0xC1);     //set contrast current
    writeData(0x9F);
  writeCommand(0xB1);     //set phase length
    writeData(0xF2);
  writeCommand(0xBB);     //set pre-charge voltage
    writeData(0x1F);
  writeCommand(0xB4);     //set VSL
    writeData(0xA0);
    writeData(0xFD);
  writeCommand(0xBE);     //set VCOMH
    writeData(0x04);
  writeCommand(0xA6);     //set display mode
  writeCommand(0xAF);     //display ON
}

In luma/oled/device.py in the SSD1322 init function there are many differences to this, so I am wondering if this is the culprit of my problems?

self.command(0xFD, 0x12)        # Unlock IC
        self.command(0xA4)              # Display off (all pixels off)
        self.command(0xB3, 0xF2)        # Display divide clockratio/freq
        self.command(0xCA, 0x3F)        # Set MUX ratio
        self.command(0xA2, 0x00)        # Display offset
        self.command(0xA1, 0x00)        # Display start Line
        self.command(0xA0, 0x14, 0x11)  # Set remap & dual COM Line
        self.command(0xB5, 0x00)        # Set GPIO (disabled)
        self.command(0xAB, 0x01)        # Function select (internal Vdd)
        self.command(0xB4, 0xA0, 0xFD)  # Display enhancement A (External VSL)
        self.command(0xC7, 0x0F)        # Master contrast (reset)
        self.command(0xB9)              # Set default greyscale table
        self.command(0xB1, 0xF0)        # Phase length
        self.command(0xD1, 0x82, 0x20)  # Display enhancement B (reset)
        self.command(0xBB, 0x0D)        # Pre-charge voltage
        self.command(0xB6, 0x08)        # 2nd precharge period
        self.command(0xBE, 0x00)        # Set VcomH
        self.command(0xA6)              # Normal display (reset)
        self.command(0xA9)              # Exit partial display

        self.contrast(0x7F)             # Reset

Here is an edit I attempted but its still not working at all, the display is not activated and I am unsure about some additional steps in the initialization that I got rid of.

self.command(0xAE)              # Display off (all pixels off) self.command(0xA4)
        self.command(0xB3, 0x91)        # Display divide clockratio/freq was: self.command(0xB3, 0xF2)
        self.command(0xCA, 0x3F)        # Set MUX ratio
        self.command(0xA2, 0x00)        # Display offset
        self.command(0xAB, 0x01)        # Display start Line was: self.command(0xA1, 0x00)
        self.command(0xA0, 0x16, 0x11)  # Set remap & dual COM Line was: self.command(0xA0, 0x14, 0x11)
        self.command(0xC7, 0x0F)        # Master contrast (reset)
        self.command(0xC1, 0x9F)        # ADDITION set contrast current
        self.command(0xB1, 0xF2)        # Phase length was: self.command(0xB1, 0xF0)
        self.command(0xBB, 0x1F)        # Pre-charge voltage was: self.command(0xBB, 0x0D)
        self.command(0xB4, 0xA0, 0xFD)  # ADDITION set VSL
        self.command(0xBE, 0x04)        # Set VcomH was: self.command(0xBE, 0x00)
        self.command(0xA6)              # Normal display (reset)
        self.command(0xAF)              # Exit partial display self.command(0xA9)

The question is, do we need to differntiate this display from the other SSD1322 driven ones, is my display "special" or broken or is this init routine generally wrong for the SSD1322 controller or did I do something wrong with SPI on the RPi... I am not very clear on how fast we can drive this display (10Mhz? it sometimes worked for a few seconds at a time as seen in the video with 16Mhz)

I am glad about any pointers you can give me to get this display running and if I/we can find a solution I'll send a pull request if necessary.

rm-hull commented 6 years ago

Could you send a link to the device you bought?

Looking at the video you linked to, it looks like the screen resolution is 128x64, whereas the SSD1322 is geared for 256x64 as the default - this is the one I own:

image

Regarding the settings, it might be that you just need to alter the config and add something like:

--width=128
--height=64

for whatever the correct resolution is. If that doesn't solve it, then try jiggling the Display divide clockratio/freq and 'mux ratio' - these are the key params IIRC.

As to the SPI clock speed, I would start with a low value to start with and make sure it is stable, then increase it until it becomes unstable. I do recall that blasting some of these with high MHz is futile, and doesnt really affect the frame rate dramatically.

heavydetail commented 6 years ago

Hi, link to the datasheet is in the first post, here is the manufacturers page to the display: https://www.newhavendisplay.com/nhd2712864wdy3-p-9542.html

Yes its a 128x64 resolution device. I have tried your suggestions to no avail. (setting width, height and spi-bus-speed=500000 being the lowest we can specify). I will try around with freq and mux substituting the stuff in their datasheets, lets see. At the moment I am not sure if i somehow killed the display and will need to retest it with an arduino or so.

Are you sure the init sequence is correct? Again, I am seeing major differences in the manufacturer suggested init and the one in your device.py routine.

rm-hull commented 6 years ago

The init sequence works for the 256x64 SSD1322 device I and a few others own, and it was derived according to the SSD1322 datasheet listed here: https://luma-oled.readthedocs.io/en/latest/intro.html.

When you tried the 128x64 settings, and said "I have tried your suggestions to no avail.", what did the screen display?

If you can upload pictures of the output of the luma.examples demo.py script (this shows the reference image), this would help immensely.

heavydetail commented 6 years ago

I tried setting the screen size back when it was partially working and it didnt change anything on the output. (Even if I dont supply w/h the example output tells me its running in 128x64 )

Now the display doesnt respond at all at the moment, thats why I need to verify whether its dead before trying other things :/ Yes I am trying to get to the point of it displaying any example again.

heavydetail commented 6 years ago

So something else is kind of off here, in the datasheet it says /CS is active LOW, when I measure its at HIGH.

I tried specifiying in config spi-cs-high=false but it has no effect?

Removing the CS lead drops me back to 0.6v for some reason... which was enough to activate the chip and I could see something again, but just for a short while!? Garbage, but still - I'll keep investigating ;)

thijstriemstra commented 6 years ago

@heavydetail any news?

heavydetail commented 6 years ago

Nope. Couldn't get it to work. It wasn't the CS pin either. That one works as expected or so I think...

Next step would be to get a logic analyzer and check if everything is correctly transmitted or try to change the init routine again like I mentioned in before posts, but I couldn't get it to work, maybe/probably doing something wrong, but I checked the wiring about a hundred times.

Its weird because sometimes if I start an example and cancel and run in quick succession it would start up the display and show garbled stuff until it freezes. I am guessing that I somehow randomly hit the right init routine?

But also, I have not yet checked with I2C, maybe that works instead of SPI, wasn't my spec tho.

When it works its a great looking display tho, also the metal frame makes it quite sturdy and good to build into projects, so I'd still like to get it to work ;) If you have suggestions I am willing to try and report back. Maybe you can try to change the init routine to my suggestions according to the datasheet? I tried but maybe did it wrong.

krisgeeting commented 6 years ago

I'm attempting to use this same 128x64 NHD display with a PIC24 and can't get the display to come on either. You mentioned a few different initialization sequences- Which one did you use when you got the rotating cube to show up?

heavydetail commented 6 years ago

The original one included in luma, but it worked very seldomly too, I think that the rotating cube is hitting the init sequence sometimes or more often than other examples.

I still believe we should be using a different init sequence that someone would need to code up properly according to the datasheet since I didn't manage to or missed something. maybe you can try to replace with my/datasheet routine and check if it works for your controller.

krisgeeting commented 6 years ago

I agree with your hypothesis that it's likely the initialization sequence. I tried a variant of the initialization sequence in luma.oled and was able to get some streaking across the screen:

uDelay(200);
setCommandLock(0x12); //unlock
setSleepMode(0x00); //turn display off
setDisplayClock(0xF2);
setMUXRatio(0x3F); 
setDisplayOffset(0x00);
setDisplayStartLine(0x00);
setRemapFormat(0x14,0x11); 
setGPIO(0x00);
functionSelection(0x01); //internal VDD reg
setVSL(0xA0,0xFD);
setDefaultGrayScale();
setPhaseLength(0xF0);
setMasterContrastCurrent(0x0F);
setContrastCurrent(0x9F);
setPreCharge(0x1F);
setVCOMH(0x00);
setDisplayMode(0x02);
setSleepMode(0x01); //turn display on 
exitPartialDisplay();
uDelay(200);

nhd2 7_128x64_streaking

Maybe you can give this a shot and see if your screen becomes responsive again? I'm still tinkering with the sequence myself

kennethajonesii commented 6 years ago

I have the NHD-2.7-12864WDW3 and was able to get the SSD1322 driver to work with the following modifications of the ssd1322 class:

self.reset_display()
self.command(0xFD, 0x12)  # Unlock IC
self.command(0xAE)  # Display off
self.command(0xB3, 0x91)  # Display divide clockratio/freq
self.command(0xCA, 0x3F)  # Set MUX ratio
self.command(0xA2, 0x00)  # Display offset
self.command(0xAB, 0x01)  # Display offset
self.command(0xA0, 0x16, 0x11)  # Set remap & dual COM Line
self.command(0xC7, 0x0F)  # Master contrast (reset)
self.command(0xC1, 0x9F)  # Set contrast current
self.command(0xB1, 0xF2)  # Set default greyscale table
self.command(0xBB, 0x1F)  # Pre-charge voltage
self.command(0xB4, 0xA0, 0xFD)  # Display enhancement A (External VSL)
self.command(0xBE, 0x04)  # Set VcomH
self.command(0xA6)  # Normal display (reset)
self.command(0xAF)  # Exit partial display

I added a new method called reset_display to the device class in luma.core.device.py

def reset_display(self):
    from luma.core.util import usleep

    self._serial_interface.assert_reset() # pull low
    usleep(400)
    self._serial_interface.negate_reset() # Keep RESET pulled high
    usleep(400)

I also added usleep to luma.core.util.py

def usleep(x):
   time.sleep(x/1000000.0)

Finally, I added two new methods to bitbang class in luma.core.interface.serial.py.

def assert_reset(self):
    if self._RST is not None:
        self._gpio.output(self._RST, self._gpio.LOW)

def negate_reset(self):
    if self._RST is not None:
            self._gpio.output(self._RST, self._gpio.HIGH)

I used the following command line config:

--display=ssd1322 --interface=spi --width=256 --height=64 --spi-bus-speed=16000000

When I used a width of 128, there were horizontal bars (64bit wide) on each side of the display. This was pretty odd, since newhaven lists the display as 128x64. Once I changed the width parameter to 256 the full display was used.

sys_info_128x64

I think I'm still having issues with the fonts that came with luma.examples. I haven't tried any other fonts. I'm not sure if the default fonts are usable with 256x64 since the wiki does not confirm working with 256x64.

Here is an pic of the default font, C&C Red Alert [INET].ttf, used in sys_info.py sys_info_font_red_alert_tff

Here is an pic of the font tiny.ttf used in sys_info.py sys_info_font_tiny_tff

thijstriemstra commented 6 years ago

great quality pics btw

rm-hull commented 6 years ago

The ttf fonts need to be slightly bigger font size, else the anti aliasing will make them look like your second pic. Have a look at the terminal example for some decent fonts to use, or try and source some 8px bitmap fonts instead

rm-hull commented 6 years ago

I will take onboard your init settings and create a ssd1322_nhd subclass or something. However, your comment about 128px width and having to set with --width=256 doesn't seem right.

Could you take a pic of the display whilst running the perf_loop.py demo - this has the reference image (text and colored geometric shapes) I use to help diagnose issues

kennethajonesii commented 6 years ago

Are there any recommendations where I can source 8px bitmap fonts?

kennethajonesii commented 6 years ago

rm-hull,

Per your recommendation, I ran perfloop.py with width configured to 128 and 256. Here are the results:

--width=128 perfloop_width_128

--width=256 perfloop_width_256

I tried a total of 7 fonts. 5 of the fonts were the same fonts and sizes used in terminal.py. I download two fronts from www.dafont.com called 8bit-wonder and pixelmix. Both fonts were listed as 8px. Links to fonts 8bit-wonder and pixelmix.

Here are the results of the fonts:

tiny.ttf size=6 tiny_ttf_6px

ProggyTiny.ttf size=16 proggytiny_ttf_16px

creep.bdf size=16 creep_bdf_16px

miscfs_.ttf size=12 miscfs_ttf_12px

FreePixel.ttf size=12 freepixel_ttf_12px

8bit_wonder.ttf size=8 8bit_wonder_8px

pixelmix.ttf size=12 pixelmix_ttf_12px

rm-hull commented 6 years ago

Ok, so none of those images look right - at a guess, it looks like there is a different memory mapping in use and it is one picking one nibble rather than both.

okyeron commented 6 years ago

kennethajonesii - would you be able to post a gist or the full text here of your luma/core/util.py with your changes?

I'm having the same issues and tried your changes, but am getting errors due to usleep - I think I put it in the wrong place.

Thanks for your help.

kjones200 commented 6 years ago

You can replace usleep with:

time.sleep(x/1000000.0)

Here is the modified util.py

# -*- coding: utf-8 -*-
# Copyright (c) 2017-18 Richard Hull and contributors
# See LICENSE.rst for details.

import time

def usleep(x):
   time.sleep(x/1000000.0)

class mutable_string(object):

    def __init__(self, value):
        assert value.__class__ is str
        self.target = value

    def __getattr__(self, attr):
        return self.target.__getattribute__(attr)

    def __getitem__(self, key):
        return self.target[key]

    def __setitem__(self, key, value):
        assert value.__class__ is str
        tmp = list(self.target)
        tmp[key] = value
        self.target = "".join(tmp)

    def __delitem__(self, key):
        tmp = list(self.target)
        del tmp[key]
        self.target = "".join(tmp)

    def __len__(self):
        return len(self.target)

    def __iter__(self):
        return iter(self.target)

    def __str__(self):
        return str(self.target)

    def __repr__(self):
        return repr(self.target)

    def __eq__(self, other):
        return str(self.target) == str(other)

    def __hash__(self):
        return hash(self.target)

class observable(object):
    """
    Wraps any container object such that on inserting, updating or deleting,
    an observer is notified with a payload of the target. All other special
    name methods are passed through parameters unhindered.
    """
    def __init__(self, target, observer):
        self.target = target
        self.observer = observer
        self.observer(self.target)

    def __getattr__(self, attr):
        return self.target.__getattribute__(attr)

    def __getitem__(self, key):
        return self.target.__getitem__(key)

    def __setitem__(self, key, value):
        self.target.__setitem__(key, value)
        self.observer(self.target)

    def __delitem__(self, key):
        self.target.__delitem__(key)
        self.observer(self.target)

    def __len__(self):
        return self.target.__len__()

    def __iter__(self):
        return self.target.__iter__()

    def __str__(self):
        return self.target.__str__()

    def __repr__(self):
        return self.target.__repr__()
okyeron commented 6 years ago

Thanks. That helps a bunch. I was missing: import time

okyeron commented 6 years ago

Did you keep these clear/show functions after the init sequnce in device.py? self.clear() self.show()

kjones200 commented 6 years ago

Here is the entire mod of the ssd1322:

self.reset()
        self.command(0xFD, 0x12)  # Unlock IC
        self.command(0xAE)  # Display off
        self.command(0xB3, 0x91)  # Display divide clockratio/freq
        self.command(0xCA, 0x3F)  # Set MUX ratio
        self.command(0xA2, 0x00)  # Display offset
        self.command(0xAB, 0x01)  # Display offset
        self.command(0xA0, 0x16, 0x11)  # Set remap & dual COM Line
        self.command(0xC7, 0x0F)  # Master contrast (reset)
        self.command(0xC1, 0x9F)  # Set contrast current
        self.command(0xB1, 0xF2)  # Set default greyscale table
        self.command(0xBB, 0x1F)  # Pre-charge voltage
        self.command(0xB4, 0xA0, 0xFD)  # Display enhancement A (External VSL)
        self.command(0xBE, 0x04)  # Set VcomH
        self.command(0xA6)  # Normal display (reset)
        self.command(0xAF)  # Exit partial display

        self.contrast(0x7F)  # Reset
        self.clear()
        self.show()
okyeron commented 6 years ago

Thanks again. I'm still getting nothing but black screen.

If I run a python script with the default SSD1306 I get the screen to light up once in awhile, but not every time. Super strange. Might be a power problem as the RasPI I'm using is complaining about under-voltage

Prior to your changes I was seeing the similar compressed stuff on screen, but again, it would not work every time I tried the script. Very frustrating.

EDIT: I'm also looking at some C code for this display and noticed this: 0xAB, 0x01 # Select external VDD And I am using the external power pin (labeled IDD on the datasheet). I tried this just now, but no change. Gotta solve my power issue first I guess.

kjones200 commented 6 years ago

What is the manufacture and brand of your display?

What are your command line arguments?

Can you post a pic of your wiring?

Can you post you ssd1322 class?

okyeron commented 6 years ago

Same display as what you posted NHD-2.7-12864WDW3

python examples/3d_box.py -d=ssd1322 -i=spi --width=128 --height=64 --gpio-reset=15 --gpio-data-command=14 --spi-bus-speed=16000000 I'll try to post a picture of wiring later when I'm in front of the pi again.

EDIT - looking at your pictures - you have pin 20 tied to ground - is that needed? The data sheet lists that pin as "MPU Interface select signal"

The ssd1322 class matches what you had previously posted. I need to re-install luma on my pi3 to re-test this so I'll

FWIW - I think some of my problem is due to power. After having this trouble I swapped the jumper on the display to "option #1" and gave the BC_VDD a 5V (4A) supply. This didn't seem to make a difference with luma, so I tried this with an fbtft framebuffer driver I was able to get the display working for a short time, but then had some other issues.

kjones200 commented 6 years ago

okernon,

According to the data sheet, pins 19 and 20 configure the MPU interface. I have my display configured 4-wire serial interface. I'm assuming your using 3-wire which I believe won't work. 3-wire mode doesn't use DC signal; however, this signal is needed since the SSD 1322 driver sends both command and data packets. Page 5 of the data sheet explains the MPU interface selections.

Change the width argument to 256. I know the NHD-2.7-12864WDY3 is 128x64; however, the SSD1322 driver is designed for 256x64. Newhaven changed from SSD1325 (designed for 128x64) to SSD1322 for their NHD-2.7-12864xxxx products around June of 2017. Essentially they are using a 256x64 controller for a 128x64 display. Using a width of 128 will make your images look like this:

40576693-43a6ccac-60c0-11e8-82d7-dbd3fceb7e1f

This is why the fonts looks crappy, only half of the pixels are being activated. I was able to get the fonts to look have way decent; however, I had to use font sizes of 16 or more. I have since abandoned the use of the NHD-2.7-12864; I didn't want to create/re-write a driver for this single display. I can confirm NHD-2.8-25664 and NHD-3.12-25664 display working beautifully with luma.oled driver.

I would start off by using a single 3.3V source and attempt to get the display working. You can power the display directly from a raspberry pi or beagle bone. Once you know the code is working, I would then try using a separate source for the BC_VDD. When using BC_VDD, it does require configuring some on-board jumpers. Reference Independent Supply Voltage for Boost Converter on page 5 of the data sheet.

okyeron commented 6 years ago

FWIW - I am using 4-wire SPI.

I'm setting this up again from scratch and will post pictures soon. I did notice from your photo that you have all the ground pins connected, whereas I was only sending pins 1 and 10 to ground.

kjones200 commented 6 years ago

If pins 19 and 20 weren't tie to logic 0 (usually ground) and VDD, then those pins are floating. You won't have a guarantee of the interface mode. Re-wire your setup and the only pins you can leave not counted are the one labeled N.C. (3, 9, 15, and 18). All VSS pins must be connect to ground.

screen shot 2018-06-20 at 2 03 48 pm

Shoot me an email if you need further assistance.

okyeron commented 6 years ago

@kennethajonesii I'm all setup again and not having any luck at all. Blank screen. Perhaps it would be best to contact you directly as this is getting far afield of the original post. I set my email to public for a bit if you could email me from my profile. Thx

audioguardo commented 6 years ago

I too just picked up an NHD-2.7-12864WDW3 and got it running on my RPi 3 B+ with similar issues. There appears to be several discrepancies in the Luma init routine when comparing to routines from other graphics libraries like U8glib/U8G2. According to U8G2's SSD1322 routine variant for the NHD-2.7 (linked here) only every second x-pixel is used, and every 4th column is addressable. So yes that is why @kennethajonesii perfloop.py doesn't look right when scaled to 256. Its trying to light pixels that are not physically connected to the COF.

I have been trying the past few days to achieve higher framerates when rendering greyscale video using PyAV to avail. Seems to cap out at ~15fps no matter the size/format of video, even animated gifs too. 30fps videos run fine in 1-bit monochrome and on the emulator however. Unsure if its a display or an SPI bottleneck.

I will try using the U8G2 routine to see if that works, but I think the fix has to be done in Luma.core .

(P.S. Im a longtime Arduino/AVR guy just now crossing over to the darkside. Still learning the ropes to Linux/Debian and what not. Kinda wished I crossed over sooner! )

Altan5ukh commented 5 years ago

Hello Guys my display was corrupted problem please tell me this code error

        self.command(0xFD, 0x12)        # Unlock IC
        self.command(0xAE)              # Display off (all pixels off)
        self.command(0xB3, 0x91)        # Display divide clockratio/freq
        self.command(0xCA, 0x3F)        # Set MUX ratio
        self.command(0xA2, 0x00)        # Display offset
        self.command(0xAB, 0x01)        
        self.command(0xA1, 0x00)        # Display start Line
        self.command(0xA0, 0x16, 0x11)  # Set remap & dual COM Line
        self.command(0xB5, 0x00)        # Set GPIO (disabled)
        self.command(0xAB, 0x01)        # Function select (internal Vdd)
        self.command(0xB4, 0xA0, 0xFD)  # Display enhancement A (External VSL)
        self.command(0xC7, 0x0F)        # Master contrast (reset)
        self.command(0xB1, 0xF2)        # Set default greyscale table
        self.command(0xB1, 0xF2)        # Phase length
        self.command(0xB4, 0xA0, 0xFD)  # Display enhancement B (reset)
        self.command(0xBB, 0x1F)        # Pre-charge voltage
        self.command(0xB6, 0x08)        # 2nd precharge period
        self.command(0xBE, 0x04)        # Set VcomH
        self.command(0xA6)              # Normal display (reset)
        #self.command(0xA1)
        self.command(0xA9)              # Exit partial display
doug-burrell commented 5 years ago

I'm working with some friends on a project and they've also chosen this ssd1322 display from NHD. I've got a fix in place whereby I've created a new class of display called ssd1322_nhd. It's a little hacky but it works. I've got this repo forked and I can make a pull request or just post my code here. If you're interested in either, please let me know.

thijstriemstra commented 5 years ago

a pull request, so we can take a look, would be nice.

rm-hull commented 5 years ago

A new version of luma.oled (v3.2.0) has been released with a specific driver for the newhaven device (ssd1322_nhd) courtesy of @doug-burrell

georgeharker commented 2 years ago

I’ve been having some issues with the same 2.7 New Haven display. Initially the modified _nhd class seemed to work. But grey ramps were messed up and anitalised text looked seriously wonky. I see that the class was writing out byte values rather than nibble encoded 4bit / 16 grey levels. After some experimentation it looks very much like there’s something interesting going on here.

I /think/ every other nibble controls the alternate scan of the pixel. Very strange. Anyway if I write out data for wxh (rather than wxh/2) and duplicate every pixel into both the upper and lower nibble everything looks great - the ramps now display correctly and text renders as expected - where’s previously values like 240 (prior to send to device) would snap to an incredibly low display value which looks effectively black).

If I write a zero nibble then a 4 but it displays dim but full screen. Same if I write a high nibble then zero. So clearly it needs 8bits per pixel. But it’s doing something with both the top and bottom nibble for that same pixel- seemingly it’s a dual scan on the pixel which I don’t get with the mechanics of how oleds work for precharge cycles etc. but nonetheless it appears to work a /lot/ better.

So sorry to reopen this but I think the nhd init routine / display routines are still wrong for things that aren’t pure black / white.

00F285F5-B0E5-4D09-A6EB-DA6C0E3E02B2

this is the working ramps. I can upload more images to show the diffs as needed.

I will try and tidy up the copious init routine hacks and submit a PR.

George