Ghosting problem with LCD Keypad Shield

Hi

I am using a LCD Keypad Shield with Mega 2560. I am trying to write some code that moves between different menus when Select Button is pressed. While on each menu the Up, Down, Left and Right buttons perform menu specific actions.

The problem I am having is that when the LCD is suppose to show the first menu it shows the second menu too and there is ghosting effect on the LCD screen.

I am using a variable called menu_stage. The value of this variable changes when the Select button is pressed. The Loop function uses switch case statement to call functions depending on the value of menu_stage.

Here is my code so far:

#include <LiquidCrystal.h>

int timer_default_mins = 30;

int menu_stage = 0;

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
 
//values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5
 
// read the buttons
int read_LCD_buttons()
{
 adc_key_in = analogRead(0); 
 if (adc_key_in > 1000) return btnNONE;
 if (adc_key_in < 50)   return btnRIGHT;  
 if (adc_key_in < 250)  return btnUP; 
 if (adc_key_in < 450)  return btnDOWN; 
 if (adc_key_in < 650)  return btnLEFT; 
 if (adc_key_in < 850)  return btnSELECT;  
 return btnNONE;
}
 
void setup()
{
 lcd.begin(16, 2);
}

void loop()
{
  switch (menu_stage)
 {
   case 0:
     {
     first_menu();
     }
   case 1:
     {
     second_menu();
     }
  }
}

void first_menu()
{
  lcd.setCursor(0,0);
  lcd.print("  TIMER SETUP   ");
  
  lcd.setCursor(0,1);
  lcd.print("Mins = ");
  
  lcd.setCursor(7,1);
  
  lcd_key = read_LCD_buttons();

switch (lcd_key)
 {
   case btnUP:
     {
     timer_default_mins = timer_default_mins + 1;
     lcd.print(timer_default_mins);
     lcd.print("                ");
     delay(500); //debounce
     break;
     }
   case btnDOWN:
     {
     timer_default_mins = timer_default_mins - 1;
     lcd.print(timer_default_mins);
     lcd.print("                ");
     delay(500); //debounce
     break;
     }
   case btnSELECT:
     {
     menu_stage = 1;
     delay(500); //debounce
     break;
     }
   case btnNONE:
     {
     lcd.print(timer_default_mins);
     break;
     }
}
} 
void second_menu()
{

  lcd.setCursor(0,0);
  lcd.print("  SECOND MENU   ");
  
  lcd.setCursor(0,1);
  lcd.print("      TEST      ");
  
  lcd_key = read_LCD_buttons();
 
switch (lcd_key)
 {
   case btnUP:
     {
       //code for UP button will go here
     break;
     }
   case btnDOWN:
     {
       //code for DOWN button will go here
     break;
     }
   case btnSELECT:
     {
       //code for SELECT button will go here
     break;
     }
   case btnNONE:
     {
     break;
     }
}
}

Could someone please let me know where I am going wrong with this?

Ghosting on a 1602 display ?

You're not erasing in any way (there's multiple ways of doing that).
So why are you surprised the LCD is only updated (<- keyword) on the spots where you are writing ?

What you are calling ghosting, probably (guessing here) is because of the way you are sending data to your screen.
You're deciding what you're going to send to your screen over and over again, at this point your sketch doesn't do anything else.
And you're always, every iteration, sending something to your screen.
These LCD's are relatively slow.
They aren't ready writing at the moment you are sending new data.
That is probably what you describe as ghosting (it isn't).

To solve this, only update your LCD when something has happened.
Something happens if a different key is pressed as last (or no-) key.
Only at that time it's time to update your screen.

Combine these two remarks of mine in your new sketch, and both your problems will probably disappear.

Hi MAS3

Thanks for your reply. The information you have provided is really useful. I spotted the problem in my code and have fixed it now. I missed the breaks at the end of each case statement in the main loop.

Here is the revised main loop code.

void loop()
{
  switch (menu_stage)
 {
   case 0:
     {
     first_menu();
     break;
     }
   case 1:
     {
     second_menu();
     break;
     }
  }
}

I am basically designing an interface to control a football scoreboard. I am now stuck with a new problem.

I am taking out the bit of the code that I am having issues with for ease of understanding.

After the length of the match has been setup the user gets a screen that shows home and away scores. There is a left or right pointer in the middle of the score driven by left and right buttons on the LCD Keypad Shield. What I am trying to achieve is that when the left button is pressed the pointer turns left and it turns right when the right button is pressed. The home and away score goes up and down when the up and down buttons are pressed. The role of the pointer is to determine whether the up and down buttons change the home score or away score. To handle this I have created two variables pointer_direction_home and pointer_direction_away with 1 and 0 initial values respectively. These values change as follows:

RIGHT KEY:
pointer_direction_home = 0
pointer_direction_away = 1

LEFT KEY:
pointer_direction_home = 1
pointer_direction_away = 0

The couple of problems I am having are:

  • Despite setting the initial value of pointer_direction_home = 1 the pointer does not show up on the screen at start. Pressing left or right button makes it appear though.
  • The IF statements in the btnUP and btnDOWN case only update the home score.

Here is my code so far:

#include <LiquidCrystal.h>

int home_score = 0;
int away_score = 0;

int pointer_direction_home = 1;
int pointer_direction_away = 0;

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
 
//values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5
 
// read the buttons
int read_LCD_buttons()
{
 adc_key_in = analogRead(0); 
 if (adc_key_in > 1000) return btnNONE;
 if (adc_key_in < 50)   return btnRIGHT;  
 if (adc_key_in < 250)  return btnUP; 
 if (adc_key_in < 450)  return btnDOWN; 
 if (adc_key_in < 650)  return btnLEFT; 
 if (adc_key_in < 850)  return btnSELECT;  
 return btnNONE;
}
 
void setup()
{
 lcd.begin(16, 2);
}

void loop()
{
  byte rightpointer[8] = {
  B10000,
  B11000,
  B11100,
  B11110,
  B11100,
  B11000,
  B10000,
  };
  lcd.createChar(0, rightpointer);
  
  byte leftpointer[8] = {
  B00001,
  B00011,
  B00111,
  B01111,
  B00111,
  B00011,
  B00001,
  };  
  lcd.createChar(1, leftpointer);
  
  lcd_key = read_LCD_buttons();
 
switch (lcd_key)
 {
   case btnRIGHT:
     {
     lcd.setCursor(7,1);
     lcd.print(" ");
     lcd.setCursor(8,1);
     lcd.write((byte)0);
     pointer_direction_home = 0;
     pointer_direction_away = 1;
     delay(500);
     break;
     }
   case btnLEFT:
     {
     lcd.setCursor(7,1);
     lcd.write((byte)1);
     lcd.setCursor(8,1);
     lcd.print(" ");
     pointer_direction_home = 1;
     pointer_direction_away = 0;
     delay(500);
     break;
     }
   case btnUP:
     {
      if (pointer_direction_home = 1) {  
      home_score = home_score + 1;
      delay(500);
      }
      else if (pointer_direction_away = 1) {
      away_score = away_score + 1;
      delay(500);
      }
     break;
     }
   case btnDOWN:
     {
      if (pointer_direction_home = 1) {  
      home_score = home_score - 1;
      delay(500);
      }
      else if (pointer_direction_away = 1) {
      away_score = away_score - 1;
      delay(500);        
      }
     }
   case btnSELECT:
     {
//     menu_stage = 2;
//     delay(500); //debounce
     break;
     }
   case btnNONE:
     {
     break;
     }
}
 
  lcd.setCursor(0,0);
  lcd.print("  HOME    AWAY  ");
  lcd.setCursor(0,1);
  lcd.print("   ");
  
  if (home_score < 10){
  lcd.setCursor(3,1);
  lcd.print(0);  
  lcd.setCursor(4,1);
  lcd.print(home_score);  
  }
  else if (home_score >=10){
  lcd.setCursor(3,1);
  lcd.print(home_score);    
  }
  
  lcd.setCursor(5,1);
  lcd.print("  ");

  lcd.setCursor(9,1);
  lcd.print("  ");
  
  if (away_score < 10){
  lcd.setCursor(11,1);
  lcd.print(0);  
  lcd.setCursor(12,1);
  lcd.print(away_score);  
  }
  else if (away_score >=10){
  lcd.setCursor(11,1);
  lcd.print(away_score);    
  }
  
  lcd.setCursor(13,1);
  lcd.print("   ");
}

See if this code works for you.

  1. Since you are trying to read lcd_key at the begining of the loop, the original vale of btnRight is overwritten with btnNone, and therefore nothing is printed.
    The way the code I attached is laid out will first print your pointer and only then it will scan the buttons for any changes.

  2. IF statements use logic ops for comparison.
    'if (pointer_direction_home = 1)' should be 'if (pointer_direction_home == 1) '. You should look up how to use the if statement. But in case if you already know this, then don't feel bad, as missing that double-equal sign often happens to very experienced programmers as well :slight_smile:

#include <LiquidCrystal.h>

int home_score = 0;
int away_score = 0;

int pointer_direction_home = 1;
int pointer_direction_away = 0;

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
 
//values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5
 
// read the buttons
int read_LCD_buttons()
{
 adc_key_in = analogRead(0); 
 if (adc_key_in > 1000) return btnNONE;
 if (adc_key_in < 50)   return btnRIGHT;  
 if (adc_key_in < 250)  return btnUP; 
 if (adc_key_in < 450)  return btnDOWN; 
 if (adc_key_in < 650)  return btnLEFT; 
 if (adc_key_in < 850)  return btnSELECT;  
 return btnNONE;
}
 
  byte rightpointer[8] = {
  B10000,
  B11000,
  B11100,
  B11110,
  B11100,
  B11000,
  B10000,
  };
  
  
  byte leftpointer[8] = {
  B00001,
  B00011,
  B00111,
  B01111,
  B00111,
  B00011,
  B00001,
  };  
  
void setup()
{
 lcd.begin(16, 2);
  lcd.createChar(0, rightpointer);
  lcd.createChar(1, leftpointer);
}

void loop()
{
switch (lcd_key)
 {
   case btnRIGHT:
     {
     lcd.setCursor(7,1);
     lcd.print(" ");
     lcd.setCursor(8,1);
     lcd.write((byte)0);
     pointer_direction_home = 0;
     pointer_direction_away = 1;
     delay(500);
     break;
     }
   case btnLEFT:
     {
     lcd.setCursor(7,1);
     lcd.write((byte)1);
     lcd.setCursor(8,1);
     lcd.print(" ");
     pointer_direction_home = 1;
     pointer_direction_away = 0;
     delay(500);
     break;
     }
   case btnUP:
     {
      if (pointer_direction_home == 1) {  
      home_score = home_score + 1;
      delay(500);
      }
      else if (pointer_direction_away == 1) {
      away_score = away_score + 1;
      delay(500);
      }
     break;
     }
   case btnDOWN:
     {
      if (pointer_direction_home == 1) {  
      home_score = home_score - 1;
      delay(500);
      }
      else if (pointer_direction_away == 1) {
      away_score = away_score - 1;
      delay(500);        
      }
     }
   case btnSELECT:
     {
//     menu_stage = 2;
//     delay(500); //debounce
     break;
     }
   case btnNONE:
     {
     break;
     }
}
 
  lcd_key = read_LCD_buttons();
  
  lcd.setCursor(0,0);
  lcd.print("  HOME    AWAY  ");
  lcd.setCursor(0,1);
  lcd.print("   ");
  
  if (home_score < 10){
  lcd.setCursor(3,1);
  lcd.print(0);  
  lcd.setCursor(4,1);
  lcd.print(home_score);  
  }
  else if (home_score >=10){
  lcd.setCursor(3,1);
  lcd.print(home_score);    
  }
  
  lcd.setCursor(5,1);
  lcd.print("  ");

  lcd.setCursor(9,1);
  lcd.print("  ");
  
  if (away_score < 10){
  lcd.setCursor(11,1);
  lcd.print(0);  
  lcd.setCursor(12,1);
  lcd.print(away_score);  
  }
  else if (away_score >=10){
  lcd.setCursor(11,1);
  lcd.print(away_score);    
  }
  
  lcd.setCursor(13,1);
  lcd.print("   ");
}

Works perfectly. Thanks a million.

I read up the documentation on if statements but missed out on the == detail.

Once again thanks a lot.