• We recently switched our forum platform. If you experience any issues please email support@crystalfontz.com

Help with AVR C code and CFAG12864 display

smalakar

New member
I've taken the sample code given to me by crystalfontz and have tried to convert it to code for the atmega8 microcontroller, but I don't get anything on the screen.

It's hooked up properly, a -3.5 V is on Vo, and +5V on Vdd. All we get is a full screen of turned on pixels. Also able to vary the voltage on Vo to adjust the contrast.

Here's the code, if anybody could point out what's wrong I would greatly appreciate it (for the defined constants, those are defined for what we've chose for our chip):

Code:
//===============================================
// Senior Design - AirControl
// 10/28/2004
// LCD Initialization Test
//===============================================

#include <avr/io.h>
#include <avr/delay.h>
#include <stdlib.h>

//Constants 
#define Set_RW (temp_init |= 0x02)
#define Clr_RW (temp_init &= ~0x02)

#define Set_DI (temp_init |= 0x01)
#define Clr_DI (temp_init &= ~0x01)

#define Set_E (temp_init |= 0x04)
#define Clr_E (temp_init &= ~0x04)

#define Set_CS1 (temp_init &= ~0x08) // note: Active Low! switched logic
#define Clr_CS1 (temp_init |= 0x08)

#define Set_CS2 (temp_init &= ~0x10) // note: Active Low! switched logic
#define Clr_CS2 (temp_init |= 0x10)

#define Set_RS (temp_init &= ~0x20) // note: Active Low! switched logic
#define Clr_RS (temp_init |= 0x20)

#define data_out(x) (PORTD = x)
#define reg_out (PORTB = temp_init) 


typedef short unsigned int ubyte;

void lines(void);
void Wait(int);


int main (void)
{

	while(1)
	{
		lines();
	}
	return 1;
}

	
void Wait(int us)
{
	if(us == 10)
	{
		for(int i=0; i<1000; i++)
		{
			asm ("nop");
		}
//		_delay_loop_1(27);
	}
	else if(us == 1)
	{
//		_delay_loop_1(3);
		for(int i=0; i<100; i++)
		{
			asm ("nop");
		}
	}
}

void lines(void) 
{

	DDRD = 0xFF;
	DDRB = 0XFF;
	ubyte temp_init = 0x00;

	//Idle E
	Clr_E;
	reg_out;
	
	// Talk to control register
	Clr_RS;
	reg_out;
	
	// Talk to both controllers
	Clr_CS1; 
	reg_out;
	Wait(10);
	Clr_CS2;
	reg_out;

	// Display On
	PORTD = 0x3F;
	Wait(1);
	Set_E;
	reg_out;
	Clr_E;
	reg_out;
	Wait(10);

	// Display Start Line: AAAAA : RW/DI/DB7:DB0 "0/0/11AAAAAA"
	PORTD = 0xC0;
	Wait(1);
	Set_E;
	reg_out;
	Clr_E;
	reg_out;
	Wait(10);

	int row;
  
  for(row=0;row<=7;row++)
  {
	//Set X address
	PORTD = 0xB8|row;
	Wait(1);
	Set_E;
	reg_out;
    Clr_E;
	reg_out;
    Wait(1);

    //Set Y address 0
	PORTD = 0x40;
	Wait(1);
    Set_E;
	reg_out;
    Clr_E;
	reg_out;
    Wait(1);

    //Aim at the data register.
    Set_RS;
	reg_out;
	
    //Talk to controller 1 only
    Set_CS1;
	reg_out;
    int
      col;
    for(col=0;col<=63;col++)
    {
		//FDATA(cfag12864b[row][col]);
		PORTD = 0x88;
		Set_E;
		reg_out;
		Wait(1);
		Clr_E;
		reg_out;
		Wait(1);
    }

    //Talk to the controller 2
    Clr_CS1;
	reg_out;
    Set_CS2;
	reg_out;

    for(col=64;col<=127;col++)
      {
		//FDATA(cfag12864b[row][col]);
		PORTD = 0x88;
		Set_E;
		reg_out;
		Wait(1);
		Clr_E;
		reg_out;
		Wait(1);
      }
    
    //Talk to both controllers
    Clr_CS1;
	reg_out;
    Clr_CS2;
	reg_out;

    //Aim back to the control register.
    Clr_RS;
	reg_out;
}


}
Looking for additional LCD resources? Check out our LCD blog for the latest developments in LCD technology.
 

CF Tech

Administrator
Your macros could be simplified a bit to look something like this:
Code:
#define Set_RW (PORTB =(temp_init |= 0x02))
#define Clr_RW (PORTB =(temp_init &= ~0x02))
But other than that slight optimization, I do not see anything obvious.

Is the port direction set to an output? Most micros default all the ports to inputs.

Generally at this point, I write a test loop to toggle each pin and make sure the pin you think should be toggling is changing at the LCD.
 

smalakar

New member
no luck

we tried making the most simplistic screen possible: some vertical lines. putting out $88 constantly to the DB7:DB0 pins should acheive this effect?

the data pins appear to be correct. the uC (atmel atmega8) is outputting high to db7 / db3 on the LCD, the rest of the data pins are low.

the data direction registers are set to all outputs. (DDRB = 0xFF)

we're seeing a completely 'black' screen; ie all pixels lit. is this the default state for the screen, meaning if the enable timings are incorrect, this is what you should see?
 

CF Tech

Administrator
If the screen is completely black, it could be that Vo is incorrect. What is the measured voltage on Vo with respect to ground? If it is -3.5 as your first post says, then it pretty much has to be an initialization problem.
 

smalakar

New member
Vo does measure as -3.5V with respect to ground. I think we do have it hooked up properly, when we turn the contrast up all the way, all the pixels are lit.

As far as the code goes, this is what I think the activation levels for the pins according to the datasheet are:

Code:
According to the datasheet:
DB    (high)
CS1   (low)
CS2   (low)
RS    (low)
R/~W  (high/low)
D/~I  (high/low)
E     (high)

According to the sample code:
DB    (high)
CS1   (low)
CS2   (low)
RS    (high)     <-- different
R/~W  (high/low) <-- always low
D/~I  (high/low) <-- don't use
E     (low)      <-- different
I assume the sample code works, but I've tried both sets of activation levels, and still do not get any sort of activity on the screen.

I understand that we're always writing, but I don't understand how D/~I is not being used in the sample program, and why the activation levels for reset and enable are the opposite from what the datasheet says.

Just to be sure, if i can be assured that the settings/levels/etc in the sample code are absolute, in that it is exactly what needs to happen in order to get the screen to function, then I'll just ignore the datasheet.

I was also wondering how sensitive the delays are, i'm assuming in the waveform, the times listed are the minimum required in order for the lcd to process the data.

Thanks again for the help.
 

CF Tech

Administrator
The sample code uses the PC's parallel port.

Some of the lines on the PC's parallel port are inverted by the port hardware -- writing a "1" drives the line low.

I would assume that for your hardware, none of the lines are inverted, so in all your "set" macros there should be an |= operation, and in all your "clear" there should be an &=~ operation.
 

smalakar

New member
ahhhhh ... ok .... i took "inverted at the port" to mean the at the LCD rather than the parallel port. Ok, that helps a lot, thanks.
 

smalakar

New member
one thing i just noticed ... and i got terribly confused about. I took RS to mean Reset, where I guess it's actually Register Select, and then I realized that in the sample code Reset is connected to an RC circuit? (this is mentioned in the sample code comments)

Is that right? I can't find this mentioned anywhere in the documentation. Currently I just have it set to low the whole time. Low being +5V.

If it does need to be connected to a RC circuit, does any value of R and C do?
 

CF Tech

Administrator
While the reset is held low (/RST=0, which is the same as asserted which is the same as active), the display controller will be held inactive, in its "reset" state. Bring reset high (/RST=1, which is the same as deasserted which is the same as inactive) for normal operation.

Ideally, I like to connect the reset line of the display to an I/O pin of the processor so I can give the display controller a good, hard kick in the reset at will (typically in my "init_display()" routine. This technique essentially leverages the microcontroller’s (hopefully superior) reset circuitry. In the demo code, there were not enough lines on the PC's parallel port, so we used an RC circuit instead.

The RC reset circuit assumes that the capacitor is completely discharged when power comes up (not always true, and this assumption can cause trouble). The resistor then slowly charges the capacitor, which eventually brings the pin high and releases the controller from reset.
 

smalakar

New member
Man ... well i've tried bringing reset high and low ...... and actually ... low and high ... heh. Still no good.

I'm getting pretty restless here. You've been a big help, but man ... I cannot figure out what is going on. No matter what I change in the initialization code, there really is no activity on the screen.

The most recent version of the code i'm using is here:
http://grove.ufl.edu/~smalakar/stuff/lcdnew.c
Code:
//===============================================
// Senior Design - AirControl
// 10/28/2004
// LCD Initialization Test
//===============================================

#include inttypes.h
#include avr/io.h
#include avr/delay.h
#include stdlib.h

/*********************************************************************************************
 LCD Signals according to the datasheet
 
 Signal     Description                     Pin         HIGH/LOW
 DB         Databus                         4-11        (high)
 CS1        Select Column 1 - 64            12          (low)
 CS2        Select Column 65-128            13          (low)
 RS         Reset                           14          (low)
 R/W        Read / Write                    15          (read/write)
 D/I        Data / Instruction              16          (data/instruction)
 E          Enable                          17          (H)
 
*********************************************************************************************/

// R/W      Read / Write                    15          (read/write)
#define Set_RW (PORTB |= 0x02)
#define Clr_RW (PORTB &= ~0x02)

// D/I      Data / Instruction              16          (data/instruction)
#define Set_DI (PORTB |= 0x01)
#define Clr_DI (PORTB &= ~0x01)

// E        Enable                          17          (H)
#define Set_E (PORTB |= 0x04)
#define Clr_E (PORTB &= ~0x04)

// CS1      Select Column 1 - 64            12          (low)
#define Set_CS1 (PORTB &= ~0x08) // note: Active Low! switched logic
#define Clr_CS1 (PORTB |= 0x08)

// CS2      Select Column 65-128            13          (low)
#define Set_CS2 (PORTB &= ~0x10) // note: Active Low! switched logic
#define Clr_CS2 (PORTB |= 0x10)

// RS       Reset                           14          (low)
#define Set_RS (PORTB &= ~0x20)  // note: Active Low! switched logic
#define Clr_RS (PORTB |= 0x20)

#define data_out(x) (PORTD = x)

typedef short unsigned int ubyte;

void delay_us(unsigned int);
void lines(void);


int main (void)
{
    lines();
    return 1;
}

    
void delay_us(unsigned int delay)
{
    //wait (2*delay x 4) cycles = us
    //_delay_loop_1(2 * delay);
    
    for(int i = 0; i < delay * 10; i++)
    {
        asm volatile ("nop");
    }
    
}

void lines(void) 
{
    DDRD = 0xFF;
    DDRB = 0XFF;

    //Idle E
    Clr_E;  
    
    Clr_RS; 
     delay_us(10);
    Set_RS; // Not resetting.
     delay_us(10);

    // Talk to control register
    Clr_DI;  
    
    // Talk to both controllers
    Clr_CS1;  
    Clr_CS2;  
    
    // Display On
    PORTD = 0x3F; 
     delay_us(1);
    Set_E;  
    Clr_E;  
     delay_us(10);

    // Display Start Line: AAAAA : RW/DI/DB7:DB0 "0/0/11AAAAAA"
    PORTD = 0xC0;
     delay_us(1);
    Set_E;  
    Clr_E;  
     delay_us(10);
     
    ubyte row;
    
    // Make sure we're writing.
    Clr_RW;  
     delay_us(1);
  
    for(row=0;row<=7;row++)
    {
        //Set X address
        PORTD = 0xB8|row;
         delay_us(1);
        Set_E;  
        Clr_E;  
         delay_us(1);
    
        //Set Y address 0
        PORTD = 0x40;
         delay_us(1);
        Set_E;  
        Clr_E;  
         delay_us(1);
        
        //Aim at the data register.
        Set_DI;
         delay_us(1);
        
        //Talk to controller 1 only
        Set_CS1; 
         delay_us(1);
    
        int col;
        for(col=0;col<=63;col++)
        {
            //FDATA(cfag12864b[row][col]);
            PORTD = 0xAA; 
            Set_E;
             delay_us(1);
            Clr_E;
             delay_us(1);
        }
    
        //Talk to the controller 2
        Clr_CS1; 
        Set_CS2; 
         delay_us(1);
    
        for(col=64;col<=127;col++)
        {
            //FDATA(cfag12864b[row][col]);
            PORTD = 0xAA; 
            Set_E;  
             delay_us(1);
            Clr_E;  
             delay_us(1);
        }
        
        //Talk to both controllers
        Clr_CS1; 
        Clr_CS2; 
         delay_us(1);
    
        //Aim back to the control register.
        Clr_DI; 
         delay_us(1);
        
    } // end of column loop
}
I took a picture of what we get usually.
http://grove.ufl.edu/~smalakar/stuff/lcd1.jpg
:(
(it's actually evenly filled there, the shutter must've caught it half way through a refresh or something)

No matter what we do, we always get that same screen. No activity whatsoever. We're pretty sure that the microcontroller is generating the signals, we're just outputting AA to every byte ... and every other line on the databus is high, while the others are low.

I'm really running out of ideas here, I can't find anything wrong with the code. We appreciate all the help you've given us. What could it be?? :confused:
 

CF Tech

Administrator
It is your indentation. Get that right and it will work. Give this correctly indented code a shot (download the attached file, vBulletin does some weird things):
Code:
//===============================================
// Senior Design - AirControl
// 10/28/2004
// LCD Initialization Test
//===============================================

#include inttypes.h
#include avr/io.h
#include avr/delay.h
#include stdlib.h

/*********************************************************************************************
 LCD Signals according to the datasheet
 
 Signal     Description                     Pin         HIGH/LOW
 DB         Databus                         4-11        (high)
 CS1        Select Column 1 - 64            12          (low)
 CS2        Select Column 65-128            13          (low)
 RS         Reset                           14          (low)
 R/W        Read / Write                    15          (read/write)
 D/I        Data / Instruction              16          (data/instruction)
 E          Enable                          17          (H)
 
*********************************************************************************************/

// "SET" _always_ means HIGH = 5v as you would measure with a scope
// "CLR" _always_ means LOW  = 0v as you would measure with a scope

// R/W      Read / Write                    15          (read/write)
#define Set_RW (PORTB |= 0x02)
#define Clr_RW (PORTB &= ~0x02)

// D/I      Data / Instruction              16          (data/instruction)
#define Set_DI (PORTB |= 0x01)
#define Clr_DI (PORTB &= ~0x01)

// E        Enable                          17          (H)
#define Set_E (PORTB |= 0x04)
#define Clr_E (PORTB &= ~0x04)

// CS1      Select Column 1 - 64            12          (low)
#define Set_CS1 (PORTB |= 0x08)
#define Clr_CS1 (PORTB &= ~0x08)

// CS2      Select Column 65-128            13          (low)
#define Set_CS2 (PORTB |= 0x10)
#define Clr_CS2 (PORTB &= ~0x10)

// RS       Reset                           14          (low)
#define Set_RS (PORTB |= 0x20)  // note: Active Low! switched logic
#define Clr_RS (PORTB &= ~0x20)

#define data_out(x) (PORTD = x)

typedef short unsigned int ubyte;
//===============================================
//There is only one correct way to indent. Mine.
//There is only one correct tab setting. 2.
//If your instructor says differently, they are
//wrong.
//===============================================
void delay_us(unsigned int delay)
  {
  //wait (2*delay x 4) cycles = us
  //_delay_loop_1(2 * delay);
    
  for(int i = 0; i < delay * 10; i++)
    {
    asm volatile ("nop");
    }
  }
//===============================================
void delay_ms(unsigned int delay)
  {
  for(int i = 0; i < delay; i++)
    for(int j = 0; j <= 9; j++)
      delay_us(100);
  }
//===============================================
void lines(void) 
  {
  DDRD = 0xFF;
  DDRB = 0XFF;

  //Idle E
  Clr_E;  
  // Make sure we're writing.
  Clr_RW;  
    
  //Reset the LCD controllers
  Clr_RS; 
  delay_ms(100);
  Set_RS; // Not resetting.
  delay_ms(100);

  // Talk to control register
  Clr_DI;  
    
  // Talk to both controllers
  Clr_CS1;  
  Clr_CS2;  
    
  // Display On
  PORTD = 0x3F; 
  delay_us(1);
  Set_E;  
  delay_us(1);
  Clr_E;  
  delay_ms(10);

  // Display Start Line: AAAAA : RW/DI/DB7:DB0 "0/0/11AAAAAA"
  PORTD = 0xC0;
  delay_us(1);
  Set_E;  
  delay_us(1);
  Clr_E;  
  delay_ms(10);
     
  ubyte
    row;
  for(row=0;row<=7;row++)
    {
    //Set X address
    PORTD = 0xB8|row;
    delay_us(1);
    Set_E;  
    delay_us(1);
    Clr_E;  
    delay_ms(1);
    
    //Set Y address 0
    PORTD = 0x40;
    delay_us(1);
    Set_E;  
    delay_us(1);
    Clr_E;  
    delay_ms(1);
        
    //Aim at the data register.
    Set_DI;
    delay_us(1);
        
    //Talk to controller 1 only
    Set_CS1; 
    delay_us(1);

    ubyte
      col;
    for(col=0;col<=63;col++)
      {
      //FDATA(cfag12864b[row][col]);
      PORTD = col; 
      delay_us(1);
      Set_E;
      delay_us(1);
      Clr_E;
      delay_ms(1);
      }
 
    //Talk to the controller 2
    Clr_CS1; 
    Set_CS2; 
    delay_us(1);
    
    for(col=64;col<=127;col++)
      {
      //FDATA(cfag12864b[row][col]);
      PORTD = col; 
      delay_us(1);
      Set_E;  
      delay_us(1);
      Clr_E;  
      delay_ms(1);
      }
        
    //Talk to both controllers
    Clr_CS1; 
    Clr_CS2; 
    //Aim back to the control register.
    Clr_DI; 
    delay_us(1);
    } // end of column loop
  }
//===============================================
int main (void)
  {
  lines();
  //return(1);
  //This is embedded, main() should never return. Where would
  //it return to? Instead just spin here.
  while(1);
  }
//===============================================
 

Attachments

smalakar

New member
WOW. :eek:

It works, that's awesome. Thank you SO MUCH.

Thanks again, we can finally start moving forward with our project. :D
 
Last edited:

CF Tech

Administrator
LOL you so funny, I was just being a smart aleck.

Indentation should reflect what the program is doing and make it easier to read. Once you can read it, it can be more obvious what is going on.

Compare the code carefully, I can assure you there is more than indentation changed :)

You have much to learn grasshopper.

And post a pic.

And uh, see if you can tighten up the timings, the ones I put in are very conservative.
 

smalakar

New member
haha ... me and my partner were sitting here ... and removed the thing about spacing after we realized what you had done... hah ....

thanks again. :)
 
Top