NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Writing the C code for another LCD, the EADOGM

January 27, 2010
by Frozenlock
Frozenlock's Avatar

I have the small one - EADOGM132-5

Datasheet (PDF)

Those are cheap and easy to connect little LCD screen.

The only drawback so far is the almost inexistent C coding for it. So I took a huge mug of coffee and tried... for 2 days. Whatever I do, I can't make anything appear on the screen! (Except when I cut the power, then I sometimes see a line in the screen for a second, before returning blank as before.)

I can't use the code included in the Nerdkit, because I use SPI.

Here is the code I gathered on the net and re-wrote as needed. If someone see a mistake, pleeeeeeeeease, say it! :P

1st part: the includes, variables and macros:

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include "../libnerdkits/delay.h"
#include "../libnerdkits/uart.h"

#define DISPOFF      0xAE
#define DISPON       0xAF
#define DISPSTART    0x40
#define PAGEADR      0xB0
#define COLADRL      0x00
#define COLADRH      0x10
#define contrast     0x1F
#define ADCNORMAL    0xA0
#define ADCREVERSE   0xA1
#define COMNORMAL    0xC0
#define COMREVERSE   0xC8
#define DISPNORMAL   0xA6
#define DISPREVERSE  0xA7
#define LCDBIAS9     0xA2
#define LCDBIAS7     0xA3
#define RESET        0xE2
#define SETPOWERCTRL 0x2F
#define REGRESISTOR  0x20
#define SETCONTRAST  0x81
#define STATINDMODE  0xAC
#define BOOSTERRATIO 0xF8
#define DISPLAYALLON 0xA5

uint8_t topview = 1;
uint8_t xoffset;

//------------------------------------------------------------------------     
// MCU Port
//------------------------------------------------------------------------

//Dont' forget to set them as "output"!!!

#define DOGCSPORT   PORTC
#define DOGCS       5

#define DOGRESPORT  PORTC
#define DOGRES      4

#define DOGA0PORT   PORTC
#define DOGA0       3

#define DOGSCLPORT  PORTC
#define DOGSCL      2

#define DOGSIPORT   PORTC
#define DOGSI       1

//------------------------------------------------------------------------
// Macros
//------------------------------------------------------------------------

#define SetBit(adr, bnr)    ( (adr) |=  (1 << (bnr)) )
#define ClrBit(adr, bnr)    ( (adr) &= ~(1 << (bnr)) )

#define DOGENABLE  ClrBit(DOGCSPORT, DOGCS)
#define DOGDISABLE SetBit(DOGCSPORT, DOGCS)

#define DOGCOMMAND ClrBit(DOGA0PORT, DOGA0)
#define DOGDATA    SetBit(DOGA0PORT, DOGA0)

Now the first function. It takes any of the defined Hex number and send it through the SPI wire, with the "clock" sending the necessary up-down each time.

//------------------------------------------------------------------------

void dogSPIout(int out)
{
  int msk;

  msk = 0x80;
    while(msk > 0){ 
     ClrBit(DOGSCLPORT, DOGSCL);
     if(out & msk)
        SetBit(DOGSIPORT, DOGSI);
     else
        ClrBit(DOGSIPORT, DOGSI);
     SetBit(DOGSCLPORT, DOGSCL);
     msk >>= 1;
   }

}

//------------------------------------------------------------------------

Now the LCD_ini. Depending on how the LCD is wired, the initiation might be different. Datasheet (PDF)

This is where I screw up, I think...

void initDOGM132()
{
  DOGENABLE;
  DOGCOMMAND;
  dogSPIout(DISPSTART);//+0
  if(topview)
   { xoffset = 0;
     dogSPIout(ADCNORMAL);
     dogSPIout(COMREVERSE);
   }
  else
   { xoffset = 0;
     dogSPIout(ADCREVERSE);
     dogSPIout(COMNORMAL);
   }
  dogSPIout(DISPNORMAL);
  dogSPIout(LCDBIAS9);
  dogSPIout(SETPOWERCTRL);//+7
  dogSPIout(BOOSTERRATIO);     
  dogSPIout(0);
  dogSPIout(REGRESISTOR);//+3
  dogSPIout(SETCONTRAST);          
  dogSPIout(contrast);
  dogSPIout(STATINDMODE);      
  dogSPIout(0);
  dogSPIout(DISPON);

  DOGDISABLE;
}

//------------------------------------------------------------------------

A little reset function

void ResetDOG()
{
  SetBit(DOGCSPORT,  DOGCS);
  SetBit(DOGSCLPORT, DOGSCL);
  SetBit(DOGSIPORT,  DOGSI);
  SetBit(DOGA0PORT,  DOGA0);

  ClrBit(DOGRESPORT,  DOGRES);
  delay_us(5); 
  SetBit(DOGRESPORT,  DOGRES);
}

//------------------------------------------------------------------------

And finally the main:

int main() {
  // start up the LCD
    DDRC |= (1<<PC1); //Set the pins as output
    DDRC |= (1<<PC2);
    DDRC |= (1<<PC3);
    DDRC |= (1<<PC4);
    DDRC |= (1<<PC5);
    //delay_us(2000); //Time for the LCD to boot up
    ResetDOG();
    initDOGM132();

    while(1){
        DOGENABLE;
        DOGCOMMAND;
        dogSPIout(DISPLAYALLON);
        DOGDISABLE;
        }

    return 0;
    }

My LCD is wired as the "wide range" shows in the datasheet.

January 27, 2010
by Frozenlock
Frozenlock's Avatar

Oh and my MCU is powered with 5v, so it means my outputs also are...

Can this be a problem for the 3.3v powered LCD?

Thanks ;-)

Frozenlock

January 28, 2010
by Frozenlock
Frozenlock's Avatar

Good news! After some tinkering in the C code I am now able to see the entire screen as "black".

At least now I know my LCD is working, as well as my initialization.

Next step: writing something on the screen.

January 29, 2010
by Frozenlock
Frozenlock's Avatar

Update:

I'm now able to send anything I want to the screen, weeeeeeee! (Pixel by pixel)

I have some problem with a part of my code though.. I would like to be able to find the size of a string.

Here's the code:

LCD_write_string("abcd") ;

void LCD_write_string(char input[]) 
{

uint8_t n=sizeof(input);

    if (n<3)
    {
        //A
        dogSPIout(0x00);
        dogSPIout(0x7E);
        dogSPIout(0x09);
        dogSPIout(0x09);
        dogSPIout(0x7E);
    }
    else if (n<4)
    {
        //B
        dogSPIout(0x00);
        dogSPIout(0x7F);
        dogSPIout(0x49);
        dogSPIout(0x49);
        dogSPIout(0x3E);    
    }
    else if (n<5)
    {
        //C
        dogSPIout(0x00);
        dogSPIout(0x3E);
        dogSPIout(0x41);
        dogSPIout(0x41);
        dogSPIout(0x41);    
    } 
}

It seems that no matter what I write in the string, I always obtain the condition for the letter 'A'.

Any ideas why?

January 29, 2010
by N3Roaster
N3Roaster's Avatar

The sizeof operator is used to find the size of a type or structure. As you've noticed, it cannot be used to determine the bounds of an array as the size of a pointer is always the same (char input[] is just another way of writing char *input). Furthermore, there is nothing in a C style string that says how long the string is. A C style string is, however, terminated with a null character so it is possible to know when you've hit the end of the string. The standard library has a strlen() function which can be called to obtain the length of such a string. If you always know the length of the string when you call this function (that is, you're hard coding them rather than getting them from some external source), another option is to modify your function to take a second parameter with the length of the string. That saves you the overhead of the function call or looking for the null terminator yourself.

January 29, 2010
by mrobbins
(NerdKits Staff)

mrobbins's Avatar

Hi Frozenlock,

N3Roaster is definitely pointing you in the right direction. In fact, the way the "sizeof" operator works is quite strange in C for arrays -- teachable moment! Let me show you this quick program snippet you can try:

#include <stdio.h>
#include <string.h>

void test_param_array(char x[]) {
  printf("sizeof(x) = %d\n", sizeof(x));
  printf("strlen(x) = %d\n", strlen(x));
}

int main(int argc, char **argv) {
  char b[10] = "Hello!";

  printf("sizeof(b) = %d\n", sizeof(b));
  printf("strlen(b) = %d\n", strlen(b));

  test_param_array(b);
}

And, saving that as test.c and running, here are the results

$ gcc -o test test.c; ./test
sizeof(b) = 10
strlen(b) = 6
sizeof(x) = 4
strlen(x) = 6

Notice that in both cases (the first being in the same function where the array is allocated, and the second being in another function), strlen provides the 6 that we may be looking for. But the results of sizeof are very different. In the first case, we get 10. That's because the compiler knows that the array b[] was defined to have length 10 in that same function -- so the compiler is really just replacing sizeof(b) with 10 at compile time. However, when doing the same thing in the called test_param_array function, the function could have theoretically been called with any length character array. Here, the close relationship in C between arrays and pointers comes out, and (on my 32-bit machine) I get sizeof(x) = 4, where 4 bytes is the size of a pointer! If you were to try the same code on the microcontroller, I'm fairly sure you'd see sizeof(x) = 2, because it uses 16-bit pointers.

Summary of how to fix this: depending on exactly what you're trying to do, you need to use strlen, or pass around the length of the array as a separate parameter.

Hope that helps -- let us know if this is confusing. Post pics of the LCD working!

Mike

January 29, 2010
by Frozenlock
Frozenlock's Avatar

Well, as I said, if I want to show something on the LCD, I need to write it pixel by pixel.

I want to make some kind of database with all the preconfigured letter. I.E.: The letter "A" can be shown by sending 0x00,0x7E,0x09,0x09,0x7E.

I had in mind something like this:

Function string_to_lcd("blablablabla")

Scan the string for the number of different letter. (12 in this case)

For each letter, search the ascii code in the database and print it to the LCD.

I tried with the strlen, but it only seems to work when the string is DIRECTLY in the function. number=strlen("abcd"); return the right answer, but

char x = "abcd"; number=strlen(x); doesn't.

I'll keep you informed.

January 29, 2010
by N3Roaster
N3Roaster's Avatar

First make sure that you're really passing in a pointer to your character array. The char x in your example wouldn't work except by rare coincidence since that can only hold a single character, not an array, but in this case I'm not convinced that you really need to know the length of the string ahead of time. This goes back to how strings are represented in C, as a null terminated array of char values. Thus, the string "abcd" is really something more like:

char string[5];
string[0] = 'a';
string[1] = 'b';
string[2] = 'c';
string[3] = 'd';
string[4] = '\0';

Now, you know that if you pass that to a function with a prototype something like

void string_to_lcd(char *string);

you don't know how long that string is going to be from within that function, but you do know that it should have that null character in its last position. That means that you can do something along the lines of:

int i = 0;
while(string[i] != '\0')
{
    //Find and send the pixels corresponding to the character in string[i]
    i++;
}

At the end of the loop, i will be the position of the last character in the string (the null character). Since the array starts at 0, i will also be the number of non-null characters in that string, the very number that the call to strlen() should return.

January 31, 2010
by Frozenlock
Frozenlock's Avatar

Brilliant! I understand now why Mike told you were showing me the right direction. I must say that all this "pointer" thing confuses me somehow.

I read some texts on the subject, but it still feels unnatural...

This is what I've added to your lines:

    for(j=0;j<7;j++) {

    dogSPIout(character[string[i]][j]); //Send the 8 hex of each character
    }

(character being an array with every ASCII symbol)

Well, this is what I intend to add. Of course, I hit a wall again. If I write

dogSPIout(character[32][0]);
dogSPIout(character[32][1]);
dogSPIout(character[32][2]);
dogSPIout(character[32][3]);
....
dogSPIout(character[32][7]);

everything works flawlessly and I get the character on my LCD. But if I try to make it into a for loop,

int j;
for(j=0;j<7;j++) {

  dogSPIout(character[32][j]);
}

then I have a big black line on my LCD, as if I was sending an unending "1" stream.

Again, I'll keep you informed on my progress. Thanks!

Frozenlock

January 31, 2010
by Frozenlock
Frozenlock's Avatar

Ok, I am about to throw everything by the window...

I've been working on this for days now and I always hit something that's supposed to work, but in C, it just doesn't. If I write

int i=0;
char *string =  "abcd";
dogSPIout(character[string[i]-32][0]);
dogSPIout(character[string[i]-32][1]);
dogSPIout(character[string[i]-32][2]);
dogSPIout(character[string[i]-32][3]);
....
dogSPIout(character[string[i]-32][7]);

My LCD shows an "A"

When I try to put this into another function, all hell breaks loose!!

So I have my main with the function:

LCD_write_string ("abcd");

And here is the function:

void LCD_write_string(char *string) 
{
int i=0;
dogSPIout(character[string[i]-32][0]);
dogSPIout(character[string[i]-32][1]);
dogSPIout(character[string[i]-32][2]);
dogSPIout(character[string[i]-32][3]);
....
dogSPIout(character[string[i]-32][7]);
}

Why? Whyyyyy? Is it not the SAME EXACT THING? I must have tried every single possible combination(with [], with "", with '', with (), with *, with int, with char....) but it just never works!!

January 31, 2010
by pbfy0
pbfy0's Avatar

I don't know what the -32 is doing, because that would subtract from the character and make space null. I'd just do it like this.

void lcd_write_string(PGM_P string){
    char x;
    while(pgm_read_byte(string) != 0){
        x = pgm_read_byte(string);
        dogSPIout(character[x][0]);
        dogSPIout(character[x][1]);
        dogSPIout(character[x][2]);
        dogSPIout(character[x][3]);
        dogSPIout(character[x][4]);
        dogSPIout(character[x][5]);
        dogSPIout(character[x][6]);
        dogSPIout(character[x][7]);
        string++;
    }
}

you'd need to give it a string in PSTR(), but I think your problem is you're running out of RAM, so you should give it a string in program memory

February 01, 2010
by N3Roaster
N3Roaster's Avatar

The -32 is probably just there to exploit the fact that the first 32 characters in ASCII encoding aren't printing characters, thus the character array doesn't need to waste entries on them. The bytes to send for the character 'a' would therefore be in row 65 of the table. The space wouldn't be null, it would be array index 0. (This is why I've never liked the convention of using the same symbol for 0, the null character, and the null pointer. They're very different semantically.) Assuming there's 8 bytes in the table for each of the remaining 94 printing characters, the entire character array should use 752 bytes of space. If that's all going into SRAM, that only leaves you with 272 bytes for everything else. That may or may not be problematic. I'd be interested in more details regarding

all hell breaks loose!!

ASCII table

February 01, 2010
by mrobbins
(NerdKits Staff)

mrobbins's Avatar

Hi Frozenlock,

Is it also possible that you are running into the issue where static strings in your C code (that are not defined as being in program memory) might not get copied into the compiled program? See this post from an earlier discussion where wayward explains how you can change your Makefile to include the data and bss sections of the compiled program.

Mike

February 01, 2010
by Frozenlock
Frozenlock's Avatar

Hello pbfy0, N3Roaster is right, the "-32" is because I only have the writable ascii character in my array.

I didn't though about the limited memory, I will check that! I think the characters for the NerdKit's LCD are already in an IC within the LCD, which would explain why it doesn't eat up as much space.

A possible solution to use less memory might be to use only capital letters and the 10 numbers. I'll check if it is needed soon.

For now, I'll post some long overdue pictures. First, the LCD: Front Back

Second, what the LCD shows when I put everything in the main, like this:

    char *string = "NerdKits!";
    int i= 0;
    dogSPIout(character[string[i]-32][0]); 
    dogSPIout(character[string[i]-32][1]); 
    dogSPIout(character[string[i]-32][2]); 
    dogSPIout(character[string[i]-32][3]); 
    dogSPIout(character[string[i]-32][4]); 
    dogSPIout(character[string[i]-32][5]); 
    dogSPIout(character[string[i]-32][6]); 
    dogSPIout(character[string[i]-32][7]); 
    i= 1;
    dogSPIout(character[string[i]-32][0]); 
    dogSPIout(character[string[i]-32][1]); 
    dogSPIout(character[string[i]-32][2]); 
    dogSPIout(character[string[i]-32][3]); 
    dogSPIout(character[string[i]-32][4]); 
    dogSPIout(character[string[i]-32][5]); 
    dogSPIout(character[string[i]-32][6]); 
    dogSPIout(character[string[i]-32][7]); 
    i= 2;
            ....
            ...

To write "NerdKits!", I had to go as far as i=9.

NerdKits!

And this is what happens when I try to move it into another function, you know, my "all Hell breaks loose!!" Hell!

For those wondering, the diodes in the + column is to lower the voltage from 5V to around 3.3V. I haven't try it on the 5V yet.

I will try everything you've posted here. Thank you all!!

Frozenlock

February 01, 2010
by Rick_S
Rick_S's Avatar

Another thing you might look into is placing your array into the eeprom and only reading out what you need to print what you want. That way it won't eat up flash space and the only ram used will be for what is being currently displayed....

Just a thought,

Rick

February 02, 2010
by Frozenlock
Frozenlock's Avatar

It works! I've done what pbfy0 suggested, and it worked!

I've also transferred the character array in the progmem, so now it looks like that:

static uint8_t character[][8] PROGMEM= {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},       // SP
    ...

I had to apply some minor changes to pbfy0's code in order to be able to retrieve the needed data in the array:

void LCD_write_string(PGM_P string) 
{
char x;
while(pgm_read_byte(string) != '\0'){
    x = pgm_read_byte(string);
    dogSPIout(pgm_read_word(&character[x-32][0])); 
    dogSPIout(pgm_read_word(&character[x-32][1])); 
    dogSPIout(pgm_read_word(&character[x-32][2])); 
    dogSPIout(pgm_read_word(&character[x-32][3])); 
    dogSPIout(pgm_read_word(&character[x-32][4])); 
    dogSPIout(pgm_read_word(&character[x-32][5])); 
    dogSPIout(pgm_read_word(&character[x-32][6])); 
    dogSPIout(pgm_read_word(&character[x-32][7])); 
    string++;
    }
}

Thank you very much to all of you! I would never have been able to find this on my own!

Frozenlock

February 03, 2010
by Frozenlock
Frozenlock's Avatar

I have another question...

If I want to write numbers, how can I transfer them into a string and then into the PSRT()?

February 03, 2010
by Frozenlock
Frozenlock's Avatar

Hello there! Now that my code is working, I'm able to do pretty much anything quite easily.

I can even DRAW anything I want, as long as it's 132 by 32 pixels. I use bmp2h_conv V6 to convert the bmp into an array filled with hex numbers.

Here is a little demonstration:

  1. Find a picture NerdKits
  2. Transfer it to paint, resize and save as a monochrome bmp.
  3. Use bmp2h_conv.
  4. Upload the new array in your MCU.
  5. Enjoy

NerdKits logo in the EA DOGM LCD

I have also successfully removed the diodes (3.3V) and... well... replaced it with nothing. Apparently, the LCD is able to take it's power from somewhere else, on 5V! Yay!

Also removed 3 capacitances without noticeable effects.

I still need some help to understand how to write numbers though. Example:

int num = 32;

I want the LCD to show this 32. How?

February 04, 2010
by Frozenlock
Frozenlock's Avatar

I tried the function snprintf, but it seems flawed... Here is my code:

void LCD_write_number(int number)
{
DOGENABLE;
DOGDATA;
char string_num[20]; 
snprintf(string_num, 19, "%i", number);
int i=0;
 while(string_num[i] != '\0'){
    dogSPIout(pgm_read_word(&character[string_num[i]-32][0])); 
    dogSPIout(pgm_read_word(&character[string_num[i]-32][1])); 
    dogSPIout(pgm_read_word(&character[string_num[i]-32][2])); 
    dogSPIout(pgm_read_word(&character[string_num[i]-32][3])); 
    dogSPIout(pgm_read_word(&character[string_num[i]-32][4])); 
    dogSPIout(pgm_read_word(&character[string_num[i]-32][5])); 
    dogSPIout(pgm_read_word(&character[string_num[i]-32][6])); 
    dogSPIout(pgm_read_word(&character[string_num[i]-32][7]));   
    i++;
}
DOGDISABLE;
}

It works if I directly put a number into the char, for example: char string_num = "123" But oddly, it doesn't work when I use snprintf to convert an interger to a string. Worse, the LCD ALWAYS show the same 10 characters regardless of the array's size or the number.

A little hint, please!

Frozenlock

February 04, 2010
by bretm
bretm's Avatar

I don't have a lot of experience with this, but I'd guess you're running into the same problem with strings being in RAM instead of program memory and not getting copied to the chip because they're in the .data segment instead of the .text segment.

Your formatting string "%i" should probably be PSTR("%i"), and sprintf should probably be sprintf_P.

Another way to do it is to figure out the characters yourself, one digit at a time:

int i = 20;

do
{
   string_num[--i] = '0' + (number % 10);
   number /= 10;
}
while (number != 0);

while (i < 20)
{
  dogSPIout(pgm_read_word(&character[string_num[i]-32][0])); 
  dogSPIout(pgm_read_word(&character[string_num[i]-32][1])); 
  . . .
  i++;
}
February 05, 2010
by Frozenlock
Frozenlock's Avatar

PSTR("%i") worked like a charm, thanks a lot!

February 11, 2010
by Frozenlock
Frozenlock's Avatar

Me again, with yet another problem!

Whatever I do, I'm unable to make my LCD show numbers bigger than 1024. Being a power of 2, my guess is that the source of the problem is somewhere in my variable definition, but I've failed to pinpoint the location.

February 11, 2010
by Frozenlock
Frozenlock's Avatar

My apologies, it was an hardware problem.

March 24, 2010
by Phrank916
Phrank916's Avatar

Frozenlock:

Is there any chance you might have created an LCD.h file for this display yet?

I've decided to use one of these displays for a future project: making a "bike computer". I think a graphical display like this would be best because I could have the large numbers for the current speed and then some smaller numbers showing other stats like distance traveled and time, etc.

I've decided on the bright white one from Mouser which doesn't require a backlight, since I'll really only be using it in the daytime.

So, if you don't have a drop-and-go file yet, could you share some code and maybe I could work on it? Unless it's pretty much all in the thread above. I don't have the LCD yet so I haven't really studied the code above. Although, when I do get the display, I'm very interested in NOT reinventing the wheel! heheh ;)

Ted

March 24, 2010
by Phrank916
Phrank916's Avatar

Oops that was the wrong part number, this is the one I want

March 24, 2010
by Frozenlock
Frozenlock's Avatar

Sure, here is the code I use: EA dogm code

Be sure to include the dogini2.c in your main. (#include "dogini2.c" )

You might want to search a little further. I once saw some code for the bigger LCDs which wasn't working with mine. Perhaps they would with yours...

Anyhow, the most important step is the initialization and the contrast settings. If something is wrong in those, you will have no way of knowing if your LCD is working. Also, don't forget to set it up with the proper pixel number.

In your main, if you want to write a string, use the command

LCD_write_string (PSTR("Beep!"));

If you want to write a number

LCD_write_number(variable);

Don't hesitate to ask if you have any other question.

By the way, is there a way to upload to this forum?

Post a Reply

Please log in to post a reply.

Did you know that the sparks present in motors can be dangerous for your microcontroller and other electronics? Learn more...