NHD-0220DZW-AB5 ignores RS line?!
Hello,
I've got an NHD-0220DZW-AB5 that exhibits some really bizarre behavior. I'm controlling it via the 8-bit 6800 mode. It appears that, regardless of the state of the register select line, every byte I send to the display gets interpreted as a command. It gets stranger from there, too: if I initialize the display following the exact sequence given in the datasheet (except I send the "Function Set" command multiple times, or else it gets stuck in single-line mode) and then send an ASCII "6" as display data, the display glitches out (the entire upper row appears to be all on for a moment) and random characters from the English/Russian character ROM appear on the display. If I then send it proper "function set" commands with different character ROMs selected, the characters change to the respective selected ROM. I've even got the display controller to apparently crash on occasion--when reading it, the BUSY flag is always set and never goes low.
I've double-checked all my timings with an oscilloscope, so I'm reasonably sure that I'm meeting all the timings specified in the datasheet. The process of generating garbage characters I describe above is readily repeatable, so I'd be happy to provide photos and oscilloscope captures on request. I've played with similar Newhaven 2x20 displays (LCD and VFD) before with complete success, so I really have no idea what I might be doing wrong. (It's a shame, too--the OLED display looks really lovely!)
Thanks in advance,
- Stephen
-
Hi Stephen,
The OLED displays require more delay time than our LCD character displays.
Some instructions have a max execution time of up to 600us, while the write cycle has a minimum of 500ns.
Reference this example code written for an Arduino MEGA.// Define
#define E_Pin 32
#define R_W 31
#define R_S 30
// Constant Strings
char const text1[] = (" Newhaven Display ");
char const text2[] = ("Character OLED BLUE ");
char const text3[] = (" NHD-0220DZW-AB5 ");
char const text4[] = ("Blue Character OLED ");
char const text5[] = (" Display Shift ");
char const text6[] = ("Custom Character RAM");
//Write Functions + INITIALIZATION
void command(char i){ //Write Command
PORTA = i;
digitalWrite(R_S, LOW);
digitalWrite(R_W, LOW);
digitalWrite(E_Pin, HIGH);
delay(1);
digitalWrite(E_Pin, LOW);
}
void data(char i){ //Write Data
PORTA = i;
digitalWrite(R_S, HIGH);
digitalWrite(R_W, LOW);
digitalWrite(E_Pin, HIGH);
delay(1);
digitalWrite(E_Pin, LOW);
}
void init1(){ // initialize the display
digitalWrite(E_Pin, LOW);
delay(5);
command(0x30); // Wake up 1
delay(100);
command(0x30); // Wake up 2
delay (10);
command(0x30); // Wake up 3
delay (10);
command(0x39); // Function 8bit, 2 line, 5x8 text, Western European Font Table
delay(5);
command(0x08); // Display Off
delay(5);
command(0x01); // Clear Display
delay(5);
command(0x06); // Entry Mode
delay(5);
command(0x02); // Home
delay(5);
command(0x0C); // Display & Cursor ON
delay(5);
}
void clear_screen(){ // clear display
command(0x01);
}
void ret_home(){ // Return to home position
command(0x02);
}
// SPECIAL FUNCTIONS
void sftr(){ // Shift Display
for(int i = 0; i<20 ; i++){
command(0x1C);
delay(75);
}
}
void char1(){ // Custom Character Creation
command(0x40);
data(0x1F);
data(0x15);
data(0x15);
data(0x04);
data(0x04);
data(0x15);
data(0x15);
data(0x1F);
data(0x01);
data(0x02);
data(0x04);
data(0x09);
data(0x12);
data(0x04);
data(0x08);
data(0x10);
data(0x0E);
data(0x0A);
data(0x1F);
data(0x0A);
data(0x0A);
data(0x1F);
data(0x0A);
data(0x0E);
data(0x0A);
data(0x04);
data(0x0a);
data(0x11);
data(0x0A);
data(0x04);
data(0x0A);
data(0x11);
data(0x04);
data(0x0a);
data(0x11);
data(0x0a);
data(0x04);
data(0x0a);
data(0x11);
data(0x0A);
data(0x15);
data(0x0A);
data(0x0A);
data(0x15);
data(0x15);
data(0x0A);
data(0x0A);
data(0x15);
data(0x1F);
data(0x1B);
data(0x15);
data(0x1B);
data(0x1B);
data(0x15);
data(0x1B);
data(0x1F);
}
// DISPLAY FUNCTIONS
void disp1(){
clear_screen();
delay(2);
ret_home();
delay(2);
for( int i = 0; i< 20; i++){
data(text1[i]);
}
command(0xc0); // Second Line
for (int i = 0; i<20; i++){
data(text2[i]);
}
ret_home();
}
void disp2(){
clear_screen();
delay(2);
ret_home();
delay(2);
for( int i = 0; i< 20; i++){
data(text3[i]);
}
command(0xc0); // Second Line
for (int i = 0; i<20; i++){
data(text4[i]);
}
ret_home();
}
void disp3(){
clear_screen();
delay(2);
ret_home();
delay(2);
for( int i = 0; i< 20; i++){
data(text1[i]);
}
command(0xc0); // Second Line
for (int i = 0; i<20; i++){
data(text5[i]);
}
ret_home();
}
void disp4(){
for( int i = 0; i< 20; i++){
data(text6[i]);
}
command(0xc0); // Second Line
for(int i = 0; i < 7 ; i++){
data(0x20);
}
delay(1);
data(0x00);
data(0x01);
data(0x02);
data(0x03);
data(0x04);
data(0x05);
data(0x06);
ret_home();
}
void setup() {
DDRA = 0xFF; // PORT A as output
DDRC = 0xFF; // Port C as output
init1();
}
void loop() {
disp1(); // Show Text 1
delay(5000);
disp2(); // Show Text 2
delay(5000);
disp3(); // Show Text 3
delay(1000);
sftr(); // Shift display off screen, clear display
delay(200);
char1(); // call custom character creation
delay(10);
clear_screen();
delay(5);
ret_home(););
delay(5);
disp4(); // show custom characters
delay(5000);
clear_screen();
}Best Regards,
0 -
I'm aware of the timing requirements. My process for writing to the display is:
- Set RS and R/W# as needed
- Load the byte to be written onto the GPIO port
- Pull the E line low
- Set the GPIO port to input
- Drive RS low and and R/W# high: command the display to output its status byte
- Delay loop: wait for about 10 μs
- GPIO read loop: wait for bit 7 (BUSY flag) to go low
- Set E and RS high; pull R/W# low (default state)
- Set GPIO port back to output
Here's my actual code. It's Motorola/Freescale/NXP HCS12 assembly. PORTD is connected to the data lines. PT0 is E, PT1 is R/W#, and PT2 is RS.WriteDisplayCmd:
; Assumes the command to be written is in A.
MOVB #$01, PTT ; Set display controller to be written a command
STAA PORTD ; Load the command onto the data lines
BCLR PTT, #$01 ; Pull E line low -- latch in the command
NOP ; for timing safety
CLR DDRD ; Set PORTD to input
BSET PTT, #$03 ; Set display controller to be read. PTT = $03
JSR DelayLoop
JSR BusyFlagWait ; Wait for the busy flag to be cleared
MOVB #$05, PTT ; Set display controller to be written data
MOVB #$FF, DDRD ; Set PORTD back to output
RTS
WriteDisplayData:
; Assumes the character to be displayed is in A.
MOVB #$05, PTT ; Set display controller to receive display data
STAA PORTD ; Load the command onto the data lines
BCLR PTT, #$01 ; Pull E line low -- latch in the command
JSR DelayLoop ; for timing safety
CLR DDRD ; Set PORTD to input
MOVB #$02, PTT ; Set display controller to read status byte
JSR DelayLoop ; Allow time for the controller to set its outputs
BSET PTT, #$01 ; E going high -> controller outputs data
JSR DelayLoop
JSR BusyFlagWait ; Wait for the busy flag to be cleared
MOVB #$05, PTT ; Set display controller to be written data
MOVB #$FF, DDRD ; Set PORTD back to output
RTS
BusyFlagWait:
BRSET PORTD, $80, BusyFlagWait
RTS
DelayLoop:
PSHB
LDAB #20
DelayLoopLoop:
DECB
BNE DelayLoopLoop
PULB
RTSI've measured my cycle time between most commands (i.e. the time between falling edges on the E line) as just over 18 μs. The display clear command 0x01 takes a bit over 630 μs to complete, measured from the falling edge on the E line to when the BUSY flag clears. Scope captures are available on request.
Every command proper that I've sent to the display works perfectly. But I've never, ever gotten a display character to work.0
Please sign in to leave a comment.
Comments
2 comments