luma.oled: Wrong Init Routine for SSD1322 ? / NHD-2.7-12864WD

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.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 35 (8 by maintainers)

Most upvoted comments

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

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.

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()

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