Strange SPI values when initializing display. (SOLVED)

Hi all,

I am currently attempting to initialize a Newhaven OLED display (3.12 OLED).

I am using an Arduino Pro Mini 3.3V and created a sketch following the initialization routine specified in the datasheet. From the timing diagram shown in the datasheet it appears that the clock is inactive HIGH (CPOL = 1) and data is shifted on the rising edge (CPHA = 1) resulting in a mode 3 SPI interface.

This is the timing diagram in the datasheet:

However when I set the SPI mode in the sketch to what is shown in the datasheet (Mode 3; pull up on clock line to 3.3V), I get strange values being sent over the SPI bus when I examine the data with a logic analyzer. If I set the SPI mode to mode 1 (pull down on clock line to GND) then the data is shown correctly in the logic analyzer but the communication is not what it should be so the display will not initialize. I am changing the settings in the logic analyzer program to match the SPI mode in each case so that is not the issue.

So, how to get the data to be sent correctly in the correct mode? Here is a screencap of the data when in Mode 3:
Imgur

As you can see this is incorrect data when compared to the routine in the sketch.

And here is the (correct) data when sent in Mode 1:
Imgur

Here is the Arduino sketch. It is identical in both cases with the exception of the SPI.setDataMode part.

/*
OLED Display Initialization  

This is an initialization routine for the NewHaven 3.12 Blue OLED display.
The routine is taken from the datasheet for the display. The display is
connected in 4-wire serial (SPI) mode as shown in the datasheet.

*/

#include <SPI.h>

const int displaySelect = 7;
const int displayReset = 6;
const int displayDC = 5;

void setup(){
  
  pinMode(displaySelect, OUTPUT);
  pinMode(displayReset, OUTPUT);
  pinMode(displayDC, OUTPUT);

  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV64);
  SPI.setDataMode(SPI_MODE3);
  SPI.begin();



  
/* OLED INITIALIZATION ROUTINE */
  digitalWrite(displayDC,LOW); // Set data control to COMMAND mode
  digitalWrite(displaySelect,LOW); // Select display

  
  SPI.transfer(0xFD); // Unlock Basic Commands (0x12/0x16)
  SPI.transfer(0x12|0x12); 
    
  SPI.transfer(0XAE|0x00); // Display Off (0x00/0x01)
  
  SPI.transfer(0x15); // Set column address
  SPI.transfer(0x1C); // Default 0x00
  SPI.transfer(0x5B); // Default 0x77

  SPI.transfer(0x75); // Set row address
  SPI.transfer(0x00); // Default 0x00
  SPI.transfer(0x3F); // Default 0x7F
  
  SPI.transfer(0xB3); // Set display clock @ 80 FPS
  SPI.transfer(0x91); // Default 0xD0
  
  SPI.transfer(0xCA); // Set multiplex ratio to 1/64 duty
  SPI.transfer(0x3F); // Default 0x7F (1/128 Duty)
  
  SPI.transfer(0xA2); // Set display offset
  SPI.transfer(0x00);
  
  SPI.transfer(0xA1); // Set start line
  SPI.transfer(0x00);
  
  SPI.transfer(0xA0); // Set remap format
  SPI.transfer(0x14); 
  SPI.transfer(0x11); // Default 0x01 (Disable dual COM mode)
  
  SPI.transfer(0xB5); // Disable GPIO pins input
  SPI.transfer(0x00); // Default 0x0A (GPIO pins output LOW)
  
  SPI.transfer(0xAB); // Enable internal VDD regulator
  SPI.transfer(0x01); 
  
  SPI.transfer(0xB4); // Set display enhancement A
  SPI.transfer(0xA0|0xA0); // Enable external VSL - Default 0xA2
  SPI.transfer(0x05|0xFD); // Default 0xB5
  
  SPI.transfer(0xC1); // Set segment output current (contrast)
  SPI.transfer(0x9F); // Default 0x7F
   
  SPI.transfer(0xC7); // Set scale factor of segment output current control
  SPI.transfer(0x0F); // Default 0x0F (Maximum)
  
  SPI.transfer(0xB9); // Set default linear gray scale table
  
  SPI.transfer(0xB1); // Set phase length - phase 1 as 5 clocks and phase 2 as 14 clocks
  SPI.transfer(0xE2); // Default 0x74 (7 display clocks [Phase 2] / 9 Display clocks [Phase 1]
  
  SPI.transfer(0xD1); // Set display enhancement B - enhance driving scheme capability
  SPI.transfer(0x82|0x20);
  SPI.transfer(0x20);
  
  SPI.transfer(0xBB); // Set pre-charge voltage level as 0.6*VCC
  SPI.transfer(0x1F); // Default 0x17 (0.50*VCC)
  
  SPI.transfer(0xB6); // Set precharge period - set second pre-charge period as 8 clocks
  SPI.transfer(0x08);
  
  SPI.transfer(0xBE); // Set VCOMH - set common pins deselect voltage level as 0.86*VCC
  SPI.transfer(0x07); // Default 0x04 (0.80*VCC)
  
  SPI.transfer(0xA4|0x02); // Normal display mode
  
  SPI.transfer(0xA8|0x01); // Disable partial display
  
  SPI.transfer(0xAE|0x01); // Set display on/off (0x00 -> OFF; 0x01 -> ON)
  
  SPI.transfer(0xA5); // Set entire display on
  
  digitalWrite(displaySelect,HIGH); // De-select display

}


void loop(){
//  digitalWrite(displaySelect,LOW);
//  digitalWrite(displayDC,LOW);
//  SPI.transfer(0xAE|0x00);
//  delay(500);
//  SPI.transfer(0xAE|0x01);
//  digitalWrite(displaySelect,HIGH);
  
}

It looks like there is some weird glitch with the clock when in Mode 3 with the clock pulled high while inactive. It would seem to be shifting all data bits slightly since the datastream is now starting in the wrong place. What to do about this? I am not sure where the strange clock behavior is coming from. Could it have to do with the pull-up? I also have a microSD card on this bus and I am not sure if the pull-up will interfere with it as it may use a different clock polarity - but the CardInfo sketch still works with the pull-up in place so hopefully I am OK.

Any help would be greatly appreciated!

Well that was tricky to find. :slight_smile:

The problem is here:

  pinMode(displaySelect, OUTPUT);
  pinMode(displayReset, OUTPUT);
  pinMode(displayDC, OUTPUT);

The effect of that is to immediately drop displaySelect low. So the device is now selected. And also SCK is low. Then switching SPI modes makes SCK high. So you have appeared to clock out your first bit. And then everything is out by one bit.

This gives the right results:

void setup(){
  
  digitalWrite (displaySelect, HIGH);  // make sure slave is not selected when pin goes into output mode

  pinMode(displaySelect, OUTPUT);
  pinMode(displayReset, OUTPUT);
  pinMode(displayDC, OUTPUT);

  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV64);
  SPI.setDataMode(SPI_MODE3);

Proof:

Notice the glitching on MOSI and CLOCK? But that is with ENABLE high, so the device will ignore it.

That seems to have done the trick! How did you get that proof picture?

Here is the before:
Imgur

and after (with the change you suggested):
Imgur

After doing some reading it seems that this turns on the internal pullup resistor on the chip select pin when it is first initialized as an input, ensuring it remains high when it becomes an output. Pretty simple problem all said and done! Thanks so much, it always amazes me how quickly you can pick up on things that I seem to overlook for hours. :smiley:

EDIT: Finally got the display to initialize! I realized that since the circuit is always powered on these commands do nothing unless I reset the display first. Now the real work can begin. :slight_smile:

And the logic analyzer was worth every penny.

SVFeingold:
That seems to have done the trick! How did you get that proof picture?

Screenshot of the Salaea logic analyzer - same as yours I think.

SVFeingold:
After doing some reading it seems that this turns on the internal pullup resistor on the chip select pin when it is first initialized as an input, ensuring it remains high when it becomes an output.

That's right - that's exactly what it is doing.

SVFeingold:
And the logic analyzer was worth every penny.

Indeed - very hard to debug these things without it.

What I meant was did you actually compile and load this code onto an Arduino and then use the logic analyzer to get the screenshot. Cause if so I am impressed with your dedication. :smiley:

Yes I did. Since you were kind enough to provide the code.

And I like to advance my knowledge of SPI, documented here:

I might add a note to that page to warn of your particular "gotcha". :slight_smile: