Problem initialising NHD-0420DZW
Hi guys,
I am having problems when initialising the OLED.
I am always getting a weird behaviour. Most of the times having some@ signs showing up.
Can you help me please?
main.c:
#define F_CPU 3686400UL //uc läuft mit 3,6864 MHZ Quarz
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include "lcd-routines.h"
#include <util/delay.h>
int main(void)
{
_delay_ms(5);
lcd_init(); //LCD Initialisieren
_delay_ms(5);
lcd_string("hello world");
_delay_ms(5000);
}
lcd-routines.h:
#ifndef LCD_ROUTINES_H
#define LCD_ROUTINES_H
#include <avr/pgmspace.h>
/* LCD DB4-DB7 <--> PORTA Bit PA2-PA5
* Datenleitung am uc: PA2:DB4, PA3:DB5, PA4:DB6, PA4:DB7
*/
#define LCD_PORT PORTA
#define LCD_DDR DDRA
#define LCD_DB PA2
// LCD RS <--> PORTA Bit PA0 (RS: 0=Data, 1=Command)
#define LCD_RS PA0
// LCD EN <--> PORTA Bit PA1 (EN: 1-Impuls für Daten)
#define LCD_EN PA1
////////////////////////////////////////////////////////////////////////////////
// LCD Ausführungszeiten (MS=Millisekunden, US=Mikrosekunden)
#define LCD_BOOTUP_MS 2
#define LCD_ENABLE_US 100
#define LCD_WRITEDATA_US 1
#define LCD_COMMAND_US 1
#define LCD_BUSY_MS 1
#define LCD_CLEAR_DISPLAY_MS 2
#define LCD_CURSOR_HOME_MS 2
////////////////////////////////////////////////////////////////////////////////
// Zeilendefinitionen des LCDs
#define LCD_DDADR_LINE1 0x00
#define LCD_DDADR_LINE2 0x40
#define LCD_DDADR_LINE3 0x14
#define LCD_DDADR_LINE4 0x54
////////////////////////////////////////////////////////////////////////////////
// Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.
void lcd_init( void );
////////////////////////////////////////////////////////////////////////////////
// LCD löschen
void lcd_clear( void );
////////////////////////////////////////////////////////////////////////////////
// Cursor in die 1. Zeile, 0-te Spalte
void lcd_home( void );
////////////////////////////////////////////////////////////////////////////////
// Cursor an eine beliebige Position
void lcd_setcursor( uint8_t spalte, uint8_t zeile );
////////////////////////////////////////////////////////////////////////////////
// Ausgabe eines einzelnen Zeichens an der aktuellen Cursorposition
void lcd_data( uint8_t data );
////////////////////////////////////////////////////////////////////////////////
// Ausgabe eines Strings an der aktuellen Cursorposition
// String liegt im RAM
void lcd_string( const char *data );
////////////////////////////////////////////////////////////////////////////////
// Ausgabe eines Strings an der aktuellen Cursorposition
// String liegt im Flash
void lcd_string_P( PGM_P data );
////////////////////////////////////////////////////////////////////////////////
// Definition eines benutzerdefinierten Sonderzeichens.
// data muss auf ein Array[8] mit den Zeilencodes des zu definierenden Zeichens
// zeigen, Daten liegen im RAM
void lcd_generatechar( uint8_t code, const uint8_t *data );
////////////////////////////////////////////////////////////////////////////////
// Definition eines benutzerdefinierten Sonderzeichens.
// data muss auf ein Array[8] mit den Zeilencodes des zu definierenden Zeichens
// zeigen, Daten liegen im FLASH
void lcd_generatechar_P( uint8_t code, PGM_P data );
////////////////////////////////////////////////////////////////////////////////
// Ausgabe eines Kommandos an das LCD.
void lcd_command( uint8_t data );
////////////////////////////////////////////////////////////////////////////////
// LCD Befehle und Argumente.
// Zur Verwendung in lcd_command
// Clear Display -------------- 0b00000001
#define LCD_CLEAR_DISPLAY 0x01
// Cursor Home ---------------- 0b0000001x
#define LCD_CURSOR_HOME 0x02
// Set Entry Mode ------------- 0b000001xx
#define LCD_SET_ENTRY 0x04
#define LCD_ENTRY_DECREASE 0x00
#define LCD_ENTRY_INCREASE 0x02
#define LCD_ENTRY_NOSHIFT 0x00
#define LCD_ENTRY_SHIFT 0x01
// Set Display ---------------- 0b00001xxx
#define LCD_SET_DISPLAY 0x08
#define LCD_DISPLAY_OFF 0x00
#define LCD_DISPLAY_ON 0x04
#define LCD_CURSOR_OFF 0x00
#define LCD_CURSOR_ON 0x02
#define LCD_BLINKING_OFF 0x00
#define LCD_BLINKING_ON 0x01
// Set Shift ------------------ 0b0001xxxx
#define LCD_SET_SHIFT 0x10
#define LCD_CURSOR_MOVE 0x00
#define LCD_DISPLAY_SHIFT 0x08
#define LCD_SHIFT_LEFT 0x00
#define LCD_SHIFT_RIGHT 0x01
// Set Function --------------- 0b001xxxxx
#define LCD_SET_FUNCTION 0x20
#define LCD_FUNCTION_4BIT 0x00
#define LCD_FUNCTION_8BIT 0x10
#define LCD_FT_Engl_Jap 0x08
#define LCD_FT_WEurop1 0x09
#define LCD_FT_Engl_Russ 0x0A
#define LCD_FT_WEurop2 0x0B
#define LCD_SOFT_RESET 0x30
// Set CG RAM Address --------- 0b01xxxxxx (Character Generator RAM)
#define LCD_SET_CGADR 0x40
#define LCD_GC_CHAR0 0
#define LCD_GC_CHAR1 1
#define LCD_GC_CHAR2 2
#define LCD_GC_CHAR3 3
#define LCD_GC_CHAR4 4
#define LCD_GC_CHAR5 5
#define LCD_GC_CHAR6 6
#define LCD_GC_CHAR7 7
// Set DD RAM Address --------- 0b1xxxxxxx (Display Data RAM)
#define LCD_SET_DDADR 0x80
#endif
lcd-routines.c:
#include <avr/io.h>
#include "lcd-routines.h"
#include <util/delay.h>
/////////////////////////////////////////////////////////////////////////////////
// Erzeugt einen Enable-Puls
static void lcd_enable( void ) {
LCD_PORT |= (1<<LCD_EN); // Enable auf 1 setzen
_delay_us( LCD_ENABLE_US ); // kurze Pause
LCD_PORT &= ~(1<<LCD_EN); // Enable auf 0 setzen
}
////////////////////////////////////////////////////////////////////////////////
// Sendet eine 4-bit Ausgabeoperation an das LCD
static void lcd_out( uint8_t data ) {
data &= 0xF0; // obere 4 Bit maskieren
LCD_PORT &= ~(0xF0>>(4-LCD_DB)); // Maske löschen
LCD_PORT |= (data>>(4-LCD_DB)); // Bits setzen
_delay_us(100);
lcd_enable();
}
////////////////////////////////////////////////////////////////////////////////
// Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.
void lcd_init( void ) {
_delay_ms(LCD_BOOTUP_MS);
lcd_out(LCD_SET_FUNCTION);
_delay_us(LCD_COMMAND_US);
lcd_command(LCD_SET_FUNCTION|LCD_FUNCTION_4BIT|LCD_FT_Engl_Jap);
_delay_ms(LCD_BUSY_MS);
lcd_command(LCD_SET_DISPLAY|LCD_DISPLAY_OFF|LCD_CURSOR_OFF|LCD_BLINKING_OFF);
_delay_ms(LCD_BUSY_MS);
lcd_command(LCD_CLEAR_DISPLAY);
_delay_ms(LCD_BUSY_MS);
lcd_command(LCD_SET_ENTRY|LCD_ENTRY_INCREASE|LCD_ENTRY_NOSHIFT);
_delay_ms(LCD_BUSY_MS);
lcd_command(LCD_CURSOR_HOME);
_delay_ms(LCD_BUSY_MS);
lcd_command(LCD_SET_DISPLAY|LCD_DISPLAY_ON|LCD_CURSOR_OFF|LCD_BLINKING_OFF);
_delay_ms(LCD_BUSY_MS);
}
////////////////////////////////////////////////////////////////////////////////
// Sendet ein Datenbyte an das LCD
void lcd_data( uint8_t data ) {
LCD_PORT |= (1<<LCD_RS); // RS auf 1 setzen
lcd_out( data ); // zuerst die oberen,
lcd_out( data<<4 ); // dann die unteren 4 Bit senden
_delay_us( LCD_WRITEDATA_US );
}
////////////////////////////////////////////////////////////////////////////////
// Sendet einen Befehl an das LCD
void lcd_command( uint8_t data ) {
LCD_PORT &= ~(1<<LCD_RS); // RS auf 0 setzen
lcd_out( data ); // zuerst die oberen,
lcd_out( data<<4); // dann die unteren 4 Bit senden
_delay_us(LCD_COMMAND_US );
}
////////////////////////////////////////////////////////////////////////////////
// Sendet den Befehl zur Löschung des Displays
void lcd_clear( void ) {
lcd_command( LCD_CLEAR_DISPLAY );
_delay_ms( LCD_CLEAR_DISPLAY_MS );
}
////////////////////////////////////////////////////////////////////////////////
// Sendet den Befehl: Cursor Home
void lcd_home( void ) {
lcd_command( LCD_CURSOR_HOME );
_delay_ms( LCD_CURSOR_HOME_MS );
}
////////////////////////////////////////////////////////////////////////////////
// Setzt den Cursor in Zeile y (0..3) Spalte x (0..15)
void lcd_setcursor( uint8_t x, uint8_t y ) {
uint8_t data;
switch (y) {
case 0: // 1. Zeile
data = LCD_SET_DDADR + LCD_DDADR_LINE1 + x;
break;
case 1: // 2. Zeile
data = LCD_SET_DDADR + LCD_DDADR_LINE2 + x;
break;
case 2: // 3. Zeile
data = LCD_SET_DDADR + LCD_DDADR_LINE3 + x;
break;
case 3: // 4. Zeile
data = LCD_SET_DDADR + LCD_DDADR_LINE4 + x;
break;
default:
return; // für den Fall einer falschen Zeile
}
lcd_command( data );
}
////////////////////////////////////////////////////////////////////////////////
// Schreibt einen String auf das LCD
void lcd_string( const char *data ) {
while( *data != '\0' )
lcd_data( *data++ );
}
////////////////////////////////////////////////////////////////////////////////
// Schreibt einen String auf das LCD
// String liegt direkt im Flash Speicher
void lcd_string_P( PGM_P data ) {
uint8_t tmp;
tmp=pgm_read_byte(data);
while( tmp != '\0' ) {
lcd_data( tmp );
data++;
tmp = pgm_read_byte(data);
}
}
////////////////////////////////////////////////////////////////////////////////
// Schreibt ein Zeichen in den Character Generator RAM
// Daten liegen direkt im RAM
void lcd_generatechar( uint8_t code, const uint8_t *data ) {
uint8_t i;
// Startposition des Zeichens einstellen
lcd_command( LCD_SET_CGADR | (code<<3) );
// Bitmuster übertragen
for ( i=0; i<8; i++ ) {
lcd_data( *data);
data++;
}
}
////////////////////////////////////////////////////////////////////////////////
// Schreibt ein Zeichen in den Character Generator RAM
// Daten liegen direkt im Flash-Speicher
void lcd_generatechar_P( uint8_t code, PGM_P data ) {
uint8_t i;
// Startposition des Zeichens einstellen
lcd_command( LCD_SET_CGADR | (code<<3) );
// Bitmuster übertragen
for ( i=0; i<8; i++ ) {
lcd_data( pgm_read_byte(data) );
data++;
}
}
-
I am having trouble deciphering your code, but I have some Arduino code for this display in 4-bit mode and I have posted it below:
//---------------------------------------------------------
/*
NHD_0420DZW_mega.ino
Program for writing to Newhaven Display 4 x 20 Character OLED (4-bit, 6800 mode)
(c)2014 Mike LaVine - Newhaven Display International, LLC.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
//---------------------------------------------------------
// The 4 bit data bus is connected to PORTA[3..0] of the Arduino Mega2560
int RS = 30; // RS signal connected to digital pin 30 of Arduino Mega2560
int RW = 31; // R/W signal connected to digital pin 31 of Arduino Mega2560
int E = 32; // E signal connected to digital pin 32 of Arduino Mega2560
const char text1[] = {" Newhaven Display "};
const char text2[] = {" Character OLED "};
const char text3[] = {" 4 Line x 20 Char "};
const char text4[] = {"0123456789!@#$%^&*()"};
void command(char c)
{
digitalWrite(RS, LOW);
PORTA = (c>>4);
digitalWrite(E, HIGH);
digitalWrite(E, LOW);
PORTA = c;
digitalWrite(E, HIGH);
digitalWrite(E, LOW);
}
void data(char d)
{
digitalWrite(RS, HIGH);
PORTA = (d>>4);
digitalWrite(E, HIGH);
digitalWrite(E, LOW);
PORTA = d;
digitalWrite(E, HIGH);
digitalWrite(E, LOW);
}
void toggle()
{
digitalWrite(E, HIGH);
digitalWrite(E, LOW);
}
void disp()
{
int i;
command(0x02); //Home Command [Set DDRAM address to Line 1 position 1]
delay(5);
for (i=0;i<20;i++)
{
data(text1[i]);
}
command(0xC0); //Second Line [Set DDRAM address to Line 2 position 1]
for (i=0;i<20;i++)
{
data(text2[i]);
}
command(0x94); //Third Line [Set DDRAM address to Line 3 position 1]
for (i=0;i<20;i++)
{
data(text3[i]);
}
command(0xD4); //Fourth Line [Set DDRAM address to Line 4 position 1]
for (i=0;i<20;i++)
{
data(text4[i]);
}
}
void setup()
{
DDRA = 0xFF; //set PORTA as output
PORTA = 0x00; //initialize PORTA to 0x00
pinMode(RS, OUTPUT);
pinMode(RW, OUTPUT);
pinMode(E, OUTPUT);
digitalWrite(RS, LOW);
digitalWrite(RW, LOW);
digitalWrite(E, HIGH);
PORTA = 0x2; // initialize OLED
toggle(); //
toggle(); //
PORTA = 0x8; //
toggle(); //
PORTA = 0x0; //
toggle(); //
PORTA = 0x8; //
toggle(); //
PORTA = 0x0; //
toggle(); //
PORTA = 0x1; //
toggle(); //
PORTA = 0x0; //
toggle(); //
PORTA = 0x6; //
toggle(); //
PORTA = 0x0; //
toggle(); //
PORTA = 0x2; //
toggle(); //
PORTA = 0x0; //
toggle(); //
PORTA = 0xC; //
toggle(); //
delay(10);
}
void loop()
{
disp();
delay(3000);
}This code will display text on all 4 lines of the OLED. *the delay values are in milliseconds*
0 -
August 27, 2014
Mike LaVine:
I do not see how your code will work.
I refer to Page 7 of NHD-0420DZW-AY5 data sheet revision 8. On this page it lists the Max Execution Times for each command. Most of these commands have a Max Execution Time of 600us (0.600ms). This means that after a command is sent to the display, one cannot send another command for 0.6ms. Am I reading this correctly?
Your code appears to ignor the BUSY Flag. I am doing the same thing, ignoring the busy flag. With this configuration, I understand that we must respect the Max Execution Times and install suitable waits in the code after each command. You do not seem to be doing this. What am I missing?
Newbe,
Paul Robinson
NS Canada0 -
Before I posted the code, I tested it again to make certain it is working code before I shared it here.
In regards to your execution timing question, you do not "see" me accounting for this in the code, but that is because the Arduino itself is not running that fast, and more importantly the built-in Arduino digitalWrite() functions that are present for each toggle command are fairly "slow" functions. If the execution times were not met by this code, it would not work. If you are using this code and it is not working for you, your micro must be faster than the Arduino, and possibly uses direct port manipulation rather than those built-in digitalWrite functions. If this is the case, you would need to account for this by either slowing down your micro's operating frequency, or implementing delays or wait states in your program.0 -
August 28, 2014
Michael
I do not doubt that your code worked on the (one) display you tested. I may have worked on many displays.
My code (assembly) also works with one unit I have tested, but respecting the 0.6ms delays in the data sheet means my system is too slow to display the characters I need to display. I doubt that the Arduino digitalWrite() functions operate slower than the 0.6ms (600ns) Maximum Execution Time, which it would need to do in order to satisfy this specification. Am I wrong?
My concern is design verification. I cannot manufacture a part when I am not respecting 'clearly stated' required Maximum Execution delay times shown in the published data sheet. I cannot present this in a design review and state that the design meets all specifications. I will have to use another display with significantly shorter Maximum Execution delay times. There are other faster LCD units available but I otherwise am very happy with the Newhaven OLED units and I would prefer to use them.
Is it possible that there is an error in the data sheet? How would I find out?
Later,
Paul0 -
August 28, 2014 attempt #2
Please ignore the previous posting as I made an a number of errors in the time units. I think I have corrected them here.
Michael
I do not doubt that your code worked on the (one) display you tested. I may have worked on many displays.
My code (assembly) also works with one unit I have tested, but respecting the 0.6us (corrected) delays in the data sheet means my system is too slow to display the characters I need to display. I doubt that the Arduino digitalWrite() functions operate slower than the 0.6us (corrected) (600ns) Maximum Execution Time, which it would need to do in order to satisfy this specification. Am I wrong?
My concern is design verification. I cannot manufacture a part when I am not respecting 'clearly stated' required Maximum Execution delay times shown in the published data sheet. I cannot present this in a design review and state that the design meets all specifications. I will have to use another display with significantly shorter Maximum Execution delay times. There are other faster LCD units available but I otherwise am very happy with the Newhaven OLED units and I would prefer to use them.
Is it possible that there is an error in the data sheet? How would I find out?
Later,
Paul0 -
August 28, 2014 attempt #3
Please ignore the previous TWO posting as I made errors in the time units. I think I have corrected them here. I hope this time. (It is too late at night.)
Michael
I do not doubt that your code worked on the (one) display you tested. I may have worked on many displays.
My code (assembly) also works with one unit I have tested, but respecting the 0.6ms (really corrected) delays in the data sheet means my system is too slow to display the characters I need to display. I doubt that the Arduino digitalWrite() functions operate slower than the 0.6ms (really corrected) (600us) (really corrected) Maximum Execution Time, which it would need to do in order to satisfy this specification. Am I wrong?
My concern is design verification. I cannot manufacture a part when I am not respecting 'clearly stated' required Maximum Execution delay times shown in the published data sheet. I cannot present this in a design review and state that the design meets all specifications. I will have to use another display with significantly shorter Maximum Execution delay times. There are other faster LCD units available but I otherwise am very happy with the Newhaven OLED units and I would prefer to use them.
Is it possible that there is an error in the data sheet? How would I find out?
I think my difficulty in writing this posting was caused by the difference between what the data sheet says and what I think it should be saying. I think this posting is correct now. Please accept my apology - my mind is tired.
Later,
Paul0 -
The code has not only worked with several displays I have tested here, but has also been used by many customers without issue.
The digitalWrite functions on the Arduino are notoriously slow, so this is why the code works fine for the display. Not to mention the Arduino itself is not a very fast micro (16MHz). Another thing to note is that those execution times are the maximum execution times, not the minimum, or even the typical. If your design must adhere to the maximum execution time specifications, then you would need to account for this in your program. To account for this you can use delays, although it seems you have said this is not possible for you. Otherwise, you may use the check busy flag method, which would allow for your code to run consecutively with the minimum execution times between each command. If this is not possible for you, then using a 4x20 character LCD instead may be a better option for you.0 -
Michael,
I tested your code on my arduino pro mini (16mhz clk).
In the void(disp) function, I added a digital write HIGH to the led pin just before the fourth line 20 char routine and a LOW just after:
command(0xD4); //Fourth Line [Set DDRAM address to Line 4 position 1]
digitalWrite(LED, HIGH); // turn on LED
for ( i= 0 ; i <20 ;i ++)
{
data(text4);
}
digitalWrite(LED, LOW); // turn off LED
The LED is on for ~ 525 microseconds while writing the 20 characters ( ~ 26.25 microseconds per character)
This is much shorter than 600 microseconds.
I suspect that the data sheet specification should read 600 nanoseconds (600ns) max execution time.
That would be why Paul's Assembly language code works since it would be much faster than this code.
Next, I will try an assembly language routine.
Thank you.
Peter Macdonald0 -
E-mail received from Newhaven Engineering:
-------- Original Message --------
Subject: RE: Message from Newhaven Display International, Inc.
Date: Fri, 29 Aug 2014 12:13:34 -0500
From: Atif Khan
To: 'H Paul Robinson' <p.robinson@ieee.org>
Hello Paul,
Thank you for the email, and your feedback.
The 600us value in the spec is unfortunately not an error. I know some
customers actually have used shorter wait times than what is listed, but of course I couldn't guarantee reliable operation if the specifications are not met.0
Please sign in to leave a comment.
Comments
9 comments