Jim's Graphic LCD Problem

Hey all! I’ve gotten a Crystalfontz 128x64 Graphic LCD from Jim right before the break and I’ve been digging through code to make the silly thing talk. He suggested that i post for all to see.

The model number is CFAG12864S-TMI-VT

Here’s the manual for the driver: crystalfontz.com/products/document/2498/CFAG12864STMIVT.pdf

So far I’ve initialized the GLCD in 4-bit mode(baby steps :mrgreen: ) but i’m stuck on how to write things like characters and pixels to the display. I know Jim has said he’s worked with parallel displays before and the manual is greek to me for the most part(hey, i’m only 17 i’m still learning :wink: ) I need a little help deciphering this thing if you guys have any insight for me. I’m willing to put a lot of effort into this i just need a guiding hand.

Also I managed to use the basic arduino liquidCrystal library to upload the example hello world. Too bad the library code doesn’t explain how the print() function works.

Here’s my code so far:


byte data_pins[4];
byte rs_pin;
byte enable_pin;
void setup()
{
  initDisplay(2, 3, 8, 9, 10, 11);
  send(0x50, 1);
}

void write4bits(int value)
{
  for(int i = 0; i < 4; i++)
  { 
    pinMode(data_pins*, OUTPUT);
    digitalWrite(data_pins*, (value >> i) & 0x01);
  }
  pulseEnable();
}

void send(int value, boolean mode)
{
  digitalWrite(rs_pin, mode);
  write4bits(value >> 4);
  write4bits(value);
}

void initDisplay(byte RS, byte EN, byte D4, byte D5, byte D6, byte D7)
{
  enable_pin = EN;
  rs_pin = RS;
  data_pins[0] = D4;
  data_pins[1] = D5;
  data_pins[2] = D6;
  data_pins[3] = D7;
  
  pinMode(rs_pin, OUTPUT);
  pinMode(enable_pin, OUTPUT);
  digitalWrite(rs_pin, LOW);
  digitalWrite(enable_pin, LOW);
  
  delayMicroseconds(50000);
  send(0x02, 0);
  delayMicroseconds(4500);
  send(0x20, 0);
  delayMicroseconds(150);
  send(0x20, 0);
  send(0x0F, 0);
  send(0x01, 0);
  send(0x02, 0);
}

void pulseEnable()
{
  digitalWrite(enable_pin, LOW);
  delayMicroseconds(1);
  digitalWrite(enable_pin, HIGH);
  delayMicroseconds(1);
  digitalWrite(enable_pin, LOW);
  delayMicroseconds(110);
}

void loop()
{
}

Let me know what you think or if you can lend me a hand! I’ll be continuing to work on it in the mean time!**

It has been awhile since I have played with parallel LCD displays, so I am a bit rusty! I will try to help any way I can… If the display is compatible with the Hitachi HD44780 chip than you can probably just use this library, which can make your life easy…

The LiquidCrystal class is derived from the print class. As is the HardwareSerial and SoftwareSerial class and maybe a few others. This allows for all of these classes to share a bunch of common code. Also it allows you to for example cast a pointer to any of these classes to a pointer to the Print class and use that. This allows your code to not care what you are actually printing to…

If you look at the Arduino core directory. On my machine this is something like: “C:\arduino-1.0\hardware\arduino\cores” you will find print.h with the class definition. You can also deduce a lot of it from looking at the Serial class in the online reference manual.

But in particular it allows you to do things like: MyLcd.print(MyVar, DEC); …

The Print class has at least one Virtual function, in this case: virtual size_t write(uint8_t);
This allows each implementation to provide the function to actually output one byte to the device. If you look at the file print.cpp in the folder I mentioned, you will find that all of the other functions, like the print above all end up calling through the write function. I can go into much more details about how this works if you are interested, but I don’t want to bore you talking about VTABLES and the like, unless you want more details along this line.

As you have probably deduced, one of the IO pins is used as a mode. When the Mode bit is LOW(I think), it is expecting that you are sending it a Command. When the mode is HIGH, it assumes that you are sending it normal data to be displayed at the current display location. As you noticed there are several commands defined, to do things like clear the screen, Move the cursor to home, Move the cursor to a specific location. I think they call it set the DDRAM location. It probably works in character locations. 0-15 are on first row, 16-31 are on the second row…

Got to run. Hope that helps
Kurt

I don’t understand how the print functions know what to write to the data pins. Like you said all of the functions in Print.cpp eventually make a call to write(const uint_t *buffer, size_t size) but then the write function calls itself???


size_t Print::write(const uint8_t *buffer, size_t size)
{
  size_t n = 0;
  while (size--) {
    n += write(*buffer++);
  }
  return n;
}

I’m lost at that point. Sorry to be so ignorant kurte.

Not a problem, we all had to start someplace and you have been doing a great job of it!

Actually the print class has three distinct write methods:

virtual size_t write(uint8_t) = 0; size_t write(const char *str) { return write((const uint8_t *)str, strlen(str)); } virtual size_t write(const uint8_t *buffer, size_t size);
The compiler knows which one to use, by the parameters being passed to it. Note 2 of them are marked virtual, which allows a derived class (like LiquidCrystal) to overwrite them. Note: the base class Print does not supply a default implementation for the simplest one, which simply takes one byte and outputs it to the appropriate device, so the derived class must provide an implementation. This is indicated in the header file by saying it is = 0…

As you noticed it does supply a default implementation for the version that takes a buffer and a count of how many bytes to output. For most derived classes this version is sufficient, but there may be versions, like lets say you are outputting to an I2C device where you can make use of knowing how many bytes will be output…

Kurt

So the derived class(my code) has to create the implimentation for the print class to use? Meaning my code tells print() how to write a byte to the device?

Depends on if you wish to have your own implementation or if you can simply use one that is already written, like LiquidCrystal. If it does not work for you, you can use it as an example.

Kurt

I suppose i could modify LiquidCrystal to include graphical function like setting pixels and drawing figures. I just have to figure out what has to be written to accomplish these things.

I don’t understand all the different RAMs like the DDRAM, GDRAM, IRAM, and CGRAM

There’s commands for setting these RAM addresses but i don’t know what that kind of thing is for

My suggestion is to start simple. That is first get your character display stuff to work.

DDRAM - I think is the main one you use to output characters to. I believe the addresses are in characters…

GDRAM - Is the graphics memory. There are commands that you send out 4 bytes. The first two address which 16 bits of data you will update and the next 2 bytes are 16 bits of data…

IRAM - ICON ram - I think this address allows you to update the 240 graphic symbol definitions. This allows you to download your own special symbols to be displayed

CGRAM - Character Generator Ram - Allows you to download your own character set to display… Probably won’t need to muck with here.

Kurt

Okay I’ve successfully written to the display using strings!!! The DDRAM is what i’m writing to here.

Here’s my code so far:


byte data_pins[4];
byte rs_pin;
byte enable_pin;
String Hello = "Hello World";
char carray[16];
void setup()
{
  Serial.begin(115200);
  initDisplay(2, 3, 8, 9, 10, 11);
  delay(100);
  writeString(0, 0, Hello);
}

void write4bits(int value)
{
  for(int i = 0; i < 4; i++)
  { 
    pinMode(data_pins*, OUTPUT);
    digitalWrite(data_pins*, (value >> i) & 0x01);
  }
  pulseEnable();
}

void command(int value)
{
  send(value, 0);
}

void data(int value)
{
  send(value, 1);
}

void send(int value, boolean mode)
{
  digitalWrite(rs_pin, mode);
  write4bits(value >> 4);
  write4bits(value);
}

void writeString(int x, int y, String string)
{
  string.toCharArray(carray, 16);
  
  switch(x)
  {
    case 0:
      y |= 0x80;
      break;
    case 1:
      y |= 0x90;
      break;
    case 2:
      y |= 0x88;
      break;
    case 3:
      y |= 0x98;
      break;
    default:
      break;
  }
  command(y);
  for(int x = 0; x < string.length(); x++)
    data(carray[x]);
}
    
void initDisplay(byte RS, byte EN, byte D4, byte D5, byte D6, byte D7)
{
  enable_pin = EN;
  rs_pin = RS;
  data_pins[0] = D4;
  data_pins[1] = D5;
  data_pins[2] = D6;
  data_pins[3] = D7;
  
  pinMode(rs_pin, OUTPUT);
  pinMode(enable_pin, OUTPUT);
  digitalWrite(rs_pin, LOW);
  digitalWrite(enable_pin, LOW);
  
  delayMicroseconds(50000);
  send(0x02, 0);
  delayMicroseconds(4500);
  send(0x20, 0);
  delayMicroseconds(150);
  send(0x20, 0);
  send(0x0c, 0);
  send(0x01, 0);
  send(0x06, 0);
}

void pulseEnable()
{
  digitalWrite(enable_pin, LOW);
  delayMicroseconds(1);
  digitalWrite(enable_pin, HIGH);
  delayMicroseconds(1);
  digitalWrite(enable_pin, LOW);
  delayMicroseconds(110);
}

void loop()
{
}

This allows me to write any string at any x,y coord (units in characters).

Next is figuring out the GDRAM stuff!

Thanks for all the help so far Kurt! :mrgreen:
Any tips for smoothing out what I have so far are extremely welcome!**

Did you figure out if there was a different setting for 20 x 4 characters? Using a different font. Great job so far! :smiley:

the only fonts on board by default in the CGRAM are 16x16 and 8x16 fonts. So i’d have to use the IRAM to create custom characters or draw letters pixel by pixel. Unless Kurt has some insight into this area. Right now i’m stuck with 4 lines of 16 characters.

Yep I can not think of any way by using normal text outs.

Kurt

Well i’ve figured out how to draw pixels to the display however it has to be done in groups of 1x16. This could make drawing objects like lines and circles difficult. :unamused:

Hey Kurt,

What does something like this do:


this->somevariable;

i can’t find any documentation for it and “this” is popping up in a lot of code i’ve been using for examples.

It has to do with dereferencing pointers. That is suppose you have some structure like:

typedef struct {
 unsigned int x;
  unsigned int y;
} pos;

You can declare an instance of this structure like:

pos  MyPos

In your code you can reference the two parts of the structure like:
MyPos.x or MyPos.y

But sometimes instead of working directly with a variable you wish to manipulate it through a pointer to the structure. For example this is often used to pass structures (or in C++ class objects) to functions.

Like:

void ClearPos(pos *posIn) { posIn->x = 0; posIn->y = 0; }

To pass a pointer to MyPos to this function you could do something like:

ClearPos(&MyPos);

To dereference a simple pointer to a variable you can use the * function. You can do stuff like:

   char b;   // Simple var
   char *pb; // define a pointer to a character variable
    pb = &b;  // Assigns the address of b to the pointer variable pb

    *pb = 0;  // indirectly assigns the value 0 to the value that is stored in pb... In example like: b = 0;

This type of code is often done for example to initialize arrays…

 char ab[10];
 char *pb;
  int i;

  pb = ab;   // don't need to use & as ab is an array... but could  have done &ab[0];

  // Something like this
  for (i=0; i < sizeof(ab); i++)
      ab* = 0;

  // Could be rewritten like:
  for (pb=ab; pb < (&ab + sizeof(ab)); pb++)
     *pb = 0;

  // or
  for (pb=ab; pb < (&ab + sizeof(ab));)
     *pb++ = 0;

Note: in c++ you have objects. When you are inside a method of a class you often wish to make use of member variables. The majority of the time you can simply reference the member by name, but sometimes local variables in the method may hide the class variable. So in the majority of the time, you don’t see this-> used… But for example:

class MyC {
 int x;
 int y;

 void Clear();
void set(int x, int y);
}

void MyC::Clear() {
   x = 0;  // one way
  this->y = 0; // using this
}

void set(int x, int y) {
  this->x = x;  // have to use this
  this->y = y;  //
}

There are others who can probably explain it better than I can. Here is one place I quickly found on the net: taranets.net/cgi/ts/1.37/ts. … =329;b=282

Or this pointer publib.boulder.ibm.com/infocente … plr035.htm

Hope that helps
Kurt*

That does help quite a bit!

Are there limits to 2 dimensional arrays? I’m currently using a 2d array to store offscreen data on the arduino until it needs to be drawn however when either the x or y value exceeds 32 my program stops displaying correctly. This is only allowing me to store half a screen worth of data as the array needs to be 64 rows by 32 columns.

Your biggest limiter is available memory. The Atmega328 chip has a RAM size of 2K. So assuming you are using a data type of 1 byte per element, your 64*32=2048 which is the total size of RAM. Your program probably has other variables, then there is stack space… So you need to save your data more compact. Like maybe a bit per pixel, where you will have to do a little work to save and retrieve the actual bit from a byte…

Kurt

Right now I am saving a bit per pixel. Every byte in the buffer represents eight pixels and every row is a row on the screen. Now I’m stuck as to how to draw different sections of the screen using my buffer.

Probably would need to see code to guess what is going on…

Kurt