Problem with LCD code

I have knocked up a quick prototype clock. It consists of a DS1307 RTC clock (see my other recent posting) and a standard 16x2 LCD display.

#include <LiquidCrystal.h>
#include <WProgram.h>
#include <Wire.h>
#include <DS1307.h>

int rtc[7];

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup()
{
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);
}

char* dayOfWeek[] = {"", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

void loop()
{
  RTC.get(rtc,true);

  lcd.setCursor(0,0);
  lcd.print(rtc[2]);
  lcd.print(":");
  lcd.print(rtc[1]);
  lcd.print(":");
  lcd.print(rtc[0]);
  lcd.print(" ");
  
  lcd.print(dayOfWeek[rtc[3]]);
  lcd.print("                ");
  
  lcd.setCursor(0,1);
  lcd.print(rtc[4]);
  lcd.print("/");
  lcd.print(rtc[5]);
  lcd.print("/");
  lcd.print(rtc[6]);
  lcd.print("                ");

  Serial.println("");
  delay(100);
}

The above code reads the time from the RTC, and displays it on the LCD. The more observant of you will notice there is a Serial.println(""); at the end of the main loop. If I remove this line then the LCD display is just blank, and I'm at a complete loss as to why.

I have tried the various other LCD samples, and they all work just fine, so I'm confident my LCD is working correctly.

It's been a very long time since I seriously did C programming (around 20 years) so I'm rusty, but I do remember that some odd bugs can creep in as a result of silly mistakes. I just can't spot the mistake.

Anyone able to offer a clue?

It looks to me like you are printing way more than 32 characters to the LCD. I have no idea whether that causes a problem, or not. Just an observation.

Yes I am. The output to each line varies in length depending on the time (I haven't written anything to write out 1 as 01 for example), so it meant that sometimes a character would be left over at the end of a line. In order to accommodate all possibilities I have written a series of 16 spaces at the end of each line to clear such leftovers. But this is just purely cosmetic, the bug still exists if I remove those extra space characters. I could use the lcd.clear() method, but that caused the display to flicker more than I wanted, so I'm not using it at the moment.

Someone else recently was having trouble with the LiquidCrystal library in conjunction with the DS1307, in very much the same way you describe. I can't recall the resolution, but I bet if you search the forum you'll uncover some useful info.

.andy

Just clear the lcd lines before printing. Or to avoid clearing add leading zeros of hour, minute, day and month to your code.

Umm I didn't see a Serial.begin(9600); in there ,is it?
If ts not then what is Serial.println() actually doing that your LCD needs?

@Nadir

I found that using lcd.clear(); caused the display to flicker way too much to be useful. I haven't bothered to re-code it to display zero padded digits because this is just a bit of test code to prove that the RTC is working correctly. I probably will do that now, but just as a way to reaquaint myself with aspects of the C language that I have now long forgotten. My final project won't actually include an LCD, which was why I didn't bother to do anything to format it nicely. I know that that kind of makes the original question moot, but want to try and understand what is going wrong so as to avoid similar problems in the future.

@tytower
There isn't one. Originally the code sent the time out over the serial line so I could see it on my PC, and thus did include the relevant serial initialisation etc. But I wanted to run it standalone for a while hence why I added the LCD. When I stripped out the serial code I accidentally missed the Serial.println(); at first. When I noticed it and removed it the sketch stopped working. The big question (for me anyway) is exactly that, what exactly is it doing that the LCD needs?

I would try commenting out the line above and running ,commenting out the println as well and running and then increasing the delay in case going off looking for a non existent function uses up just a bit more time . Alternatively play with the cursor positioning and maybe one of the lines is moving it past its line length.
Those two lines printing blanks is far more than the 16 spaces you have available?
Very strange -mostly when I've seen this aberrant behaviour it means the arduino program has lost its way with an overflow but I do not have much experience to call on yet.

I've tried most of that already. Even with just one lcd.println it doesn't work correctly if I take out the serial.println. I've increased the delay to 1000, no joy. If I take out the 16 spaces all that changes is that when the time rolls over from say 59 seconds to 0 seconds the line shortens by 1 character and whatever character was already at the end of that line stays there until the seconds roll back to 10.

Quite simply, with the Serial.println in place the sketch works exactly as I want it to for at least 24 hours. Something about that line causes it to work properly for some unknown reason. Without the line it fails immediately.

From a further forum search it appears that my problem isn't uncommon. There appears to be a conflict between the DS1307 library and the LiquidCrystal library. Unfortunately I'm nowhere near a competent enough C programmer at the moment to begin to tackle it.

Like I said, the only purpose behind this sketch and the hardware I'm using is to prove that the RTC is working as expected (I did build it myself as a novice from scratch hence my apprehension). If I need to leave that line in as a work around then I'm OK with that, but it would be nice to know why.

Slugsie, there is a possible fix suggested in this thread that you may want to try: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1258374756/25#25

Thanks mem, I'll give that a try tonight.

Slugsie, I tested your sketch with the modifed LiquidCrystal library and it works ok. You have some tab characters in the code to print spaces that look odd but the lcd displays the time correctly. That extra println can be removed.

Here is the modified LiquidCrystal.cpp file I used

// Based on Arduino 0017 LiquidCrystal code 
// this version has delayMicroseconds replaced by waitMicroseconds
// so interrupts are not disabled

#include "LiquidCrystal.h"

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "WProgram.h"

// similar to delayMicrseconds but does not disable interrupts
void waitMicroseconds(unsigned int us)
{
#if F_CPU >= 16000000L
      if (--us == 0)
            return;
      us <<= 2;
      us -= 2;
#else
      if (--us == 0)
            return;
      if (--us == 0)
            return;
      us <<= 1;
      us--;
#endif
      // busy wait
      __asm__ __volatile__ (
            "1: sbiw %0,1" "\n\t" // 2 cycles
            "brne 1b" : "=w" (us) : "0" (us) // 2 cycles
      );
}

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
                       uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
                       uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  init(0, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t enable,
                       uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
                       uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  init(0, rs, -1, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
                       uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
{
  init(1, rs, rw, enable, d0, d1, d2, d3, 0, 0, 0, 0);
}

LiquidCrystal::LiquidCrystal(uint8_t rs,  uint8_t enable,
                       uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
{
  init(1, rs, -1, enable, d0, d1, d2, d3, 0, 0, 0, 0);
}

void LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
                   uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
                   uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  _rs_pin = rs;
  _rw_pin = rw;
  _enable_pin = enable;
  
  _data_pins[0] = d0;
  _data_pins[1] = d1;
  _data_pins[2] = d2;
  _data_pins[3] = d3; 
  _data_pins[4] = d4;
  _data_pins[5] = d5;
  _data_pins[6] = d6;
  _data_pins[7] = d7; 

  pinMode(_rs_pin, OUTPUT);
  // we can save 1 pin by not using RW. Indicate by passing -1 instead of pin#
  if (_rw_pin != -1) { 
    pinMode(_rw_pin, OUTPUT);
  }
  pinMode(_enable_pin, OUTPUT);
  
  if (fourbitmode)
    _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
  else 
    _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS;
  
  begin(16, 1);  
}

void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
  if (lines > 1) {
    _displayfunction |= LCD_2LINE;
  }
  _numlines = lines;
  _currline = 0;

  // for some 1 line displays you can select a 10 pixel high font
  if ((dotsize != 0) && (lines == 1)) {
    _displayfunction |= LCD_5x10DOTS;
  }

  // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
  // according to datasheet, we need at least 40ms after power rises above 2.7V
  // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
  waitMicroseconds(50000); 
  // Now we pull both RS and R/W low to begin commands
  digitalWrite(_rs_pin, LOW);
  digitalWrite(_enable_pin, LOW);
  if (_rw_pin != -1) { 
    digitalWrite(_rw_pin, LOW);
  }
  
  //put the LCD into 4 bit or 8 bit mode
  if (! (_displayfunction & LCD_8BITMODE)) {
    // this is according to the hitachi HD44780 datasheet
    // figure 24, pg 46

    // we start in 8bit mode, try to set 4 bit mode
    write4bits(0x03);
    waitMicroseconds(4500); // wait min 4.1ms

    // second try
    write4bits(0x03);
    waitMicroseconds(4500); // wait min 4.1ms
    
    // third go!
    write4bits(0x03); 
    waitMicroseconds(150);

    // finally, set to 8-bit interface
    write4bits(0x02); 
  } else {
    // this is according to the hitachi HD44780 datasheet
    // page 45 figure 23

    // Send function set command sequence
    command(LCD_FUNCTIONSET | _displayfunction);
    waitMicroseconds(4500);  // wait more than 4.1ms

    // second try
    command(LCD_FUNCTIONSET | _displayfunction);
    waitMicroseconds(150);

    // third go
    command(LCD_FUNCTIONSET | _displayfunction);
  }

  // finally, set # lines, font size, etc.
  command(LCD_FUNCTIONSET | _displayfunction);  

  // turn the display on with no cursor or blinking default
  _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;  
  display();

  // clear it off
  clear();

  // Initialize to default text direction (for romance languages)
  _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
  // set the entry mode
  command(LCD_ENTRYMODESET | _displaymode);

}

/********** high level commands, for the user! */
void LiquidCrystal::clear()
{
  command(LCD_CLEARDISPLAY);  // clear display, set cursor position to zero
  waitMicroseconds(2000);  // this command takes a long time!
}

void LiquidCrystal::home()
{
  command(LCD_RETURNHOME);  // set cursor position to zero
  waitMicroseconds(2000);  // this command takes a long time!
}

void LiquidCrystal::setCursor(uint8_t col, uint8_t row)
{
  int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
  if ( row > _numlines ) {
    row = _numlines-1;    // we count rows starting w/0
  }
  
  command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}

// Turn the display on/off (quickly)
void LiquidCrystal::noDisplay() {
  _displaycontrol &= ~LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::display() {
  _displaycontrol |= LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turns the underline cursor on/off
void LiquidCrystal::noCursor() {
  _displaycontrol &= ~LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::cursor() {
  _displaycontrol |= LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turn on and off the blinking cursor
void LiquidCrystal::noBlink() {
  _displaycontrol &= ~LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::blink() {
  _displaycontrol |= LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// These commands scroll the display without changing the RAM
void LiquidCrystal::scrollDisplayLeft(void) {
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void LiquidCrystal::scrollDisplayRight(void) {
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}

// This is for text that flows Left to Right
void LiquidCrystal::leftToRight(void) {
  _displaymode |= LCD_ENTRYLEFT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This is for text that flows Right to Left
void LiquidCrystal::rightToLeft(void) {
  _displaymode &= ~LCD_ENTRYLEFT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'right justify' text from the cursor
void LiquidCrystal::autoscroll(void) {
  _displaymode |= LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'left justify' text from the cursor
void LiquidCrystal::noAutoscroll(void) {
  _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// Allows us to fill the first 8 CGRAM locations
// with custom characters
void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) {
  location &= 0x7; // we only have 8 locations 0-7
  command(LCD_SETCGRAMADDR | (location << 3));
  for (int i=0; i<8; i++) {
    write(charmap[i]);
  }
}

/*********** mid level commands, for sending data/cmds */

inline void LiquidCrystal::command(uint8_t value) {
  send(value, LOW);
}

inline void LiquidCrystal::write(uint8_t value) {
  send(value, HIGH);
}

/************ low level data pushing commands **********/

// write either command or data, with automatic 4/8-bit selection
void LiquidCrystal::send(uint8_t value, uint8_t mode) {
  digitalWrite(_rs_pin, mode);

  // if there is a RW pin indicated, set it low to Write
  if (_rw_pin != -1) { 
    digitalWrite(_rw_pin, LOW);
  }
  
  if (_displayfunction & LCD_8BITMODE) {
    write8bits(value); 
  } else {
    write4bits(value>>4);
    write4bits(value);
  }
}

void LiquidCrystal::pulseEnable(void) {
  digitalWrite(_enable_pin, LOW);
  waitMicroseconds(1);    
  digitalWrite(_enable_pin, HIGH);
  waitMicroseconds(1);    // enable pulse must be >450ns
  digitalWrite(_enable_pin, LOW);
  waitMicroseconds(100);   // commands need > 37us to settle
}

void LiquidCrystal::write4bits(uint8_t value) {
  for (int i = 0; i < 4; i++) {
    pinMode(_data_pins[i], OUTPUT);
    digitalWrite(_data_pins[i], (value >> i) & 0x01);
  }

  pulseEnable();
}

void LiquidCrystal::write8bits(uint8_t value) {
  for (int i = 0; i < 8; i++) {
    pinMode(_data_pins[i], OUTPUT);
    digitalWrite(_data_pins[i], (value >> i) & 0x01);
  }
  
  pulseEnable();
}

That's a bit weird, there are no tab characters in the sketch that I copied and pasted here, but they have been turned into tabs when posted into the forum. It should be a line of 16 space characters. Doesn't matter anyway as I've since modified my code to sprintf double digit numbers.

So, did the modified LiquidCrystal work for you?

BTW, sprintf is overkill for this kind of application, you can do the formating with somthing like this:

#include <LiquidCrystal.h>
#include <WProgram.h>
#include <Wire.h>
#include <DS1307.h>

int rtc[7];

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(14, 15, 16, 5, 4, 3, 2);  // change this for your connections!

void setup()
{
  // set up the LCD's number of rows and columns:
  lcd.begin(16, 2);
}

char* dayOfWeek[] = {"", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

void loop()
{
  RTC.get(rtc,true);

  lcd.setCursor(0,0);
  printDigits(rtc[2],':');
  printDigits(rtc[1],':');
  printDigits(rtc[0], ' ');
  lcd.print(dayOfWeek[rtc[3]]);

  lcd.setCursor(0,1);
  lcd.print(rtc[4]);
  lcd.print("/");
  lcd.print(rtc[5]);
  lcd.print("/");
  lcd.print(rtc[6]);
  lcd.print("     ");
  delay(100);
}

void printDigits(byte digits, char delimiter)
{
  if(digits < 10)
    lcd.print('0');
  lcd.print(digits,DEC);
  lcd.print(delimiter);
}

Unfortunately not, although the results aren't constant. Most of the time I get the same thing, a basically blank display with the top row ever so slightly darker than the bottom row. But one run the display worked but it was reading a constant zero time from the RTC (00:00:00 & 00/00/2000).

What is imitation the sincerest form of ? :wink:

Strange, the LCD display is functioning correctly for me using the code I posted. I have been running an enhanced version (uses the Ethernet shield to sync the time from an NTP time server) and its been working correctly for over an hour.

Are you sure you have replaced LiquidCrystal.cpp with the posted version?

Yup, as a quick test I added some completely random characters to the new LiquidCrystal.cpp and it failed to complile, removed them and it compiles fine. Just doesn't work. Weird yes, annoying yes, damned if I know how to fix it. :S

@pluggy

Not quite, you're in Lancs UK, I'm in the one in England. :smiley:

Plus if this is my imitation of you, it's probably not very flattering. :-[

did you try the sketch posted in reply #11 (using LiquidCrystal lcd(12, 11, 5, 4, 3, 2) or whatever you need for your lcd wiring)