Help! Convert byte to dec for 4 bit LCD

I have an I2C RTC connected to the arduino which outputs the time over the serial port using serial.print(second, DEC) and also a 4 bit LCD that I can print characters or strings to.

My problem is, when I try to write the time to the LCD it just comes up as garbage. Its printing out from the ascii table and not the actual numbers like I want.
The code is in part borrowed from tutorials across the internet and I understand how most of it works, but I dont know why print(second,DEC) converts the number for serial output but the bcdToDec() function in the code doesnt seem to work.

/*  RW tied to gnd
 *      
 * 
 * 
 *
 */

#include "Wire.h"
#define RTC_I2C_ADDRESS 0x51

#define LCD_RS      8      // Register select
#define LCD_EN      9      // Enable
#define LCD_D4 10      // Data bits
#define LCD_D5 11      // Data bits
#define LCD_D6 12      // Data bits
#define LCD_D7 13      // Data bits

// RTC functions

// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

void setDateDs1307(byte second,        // 0-59
                   byte minute,        // 0-59
                   byte hour,          // 1-23
                   byte dayOfMonth,     // 1-31
                   byte dayOfWeek,    // 1-7
                   byte month,         // 1-12
                   byte year)          // 0-99
{
   Wire.beginTransmission(RTC_I2C_ADDRESS);
   Wire.send(0);
   Wire.send(0x00);
   Wire.send(0x00);
   Wire.send(decToBcd(second));    // 0 to bit 7 starts the clock
   Wire.send(decToBcd(minute));
   Wire.send(decToBcd(hour));      // If you want 12 hour am/pm you need to set
                                   // bit 6 (also need to change readDateDs1307)
   Wire.send(decToBcd(dayOfMonth));
   Wire.send(decToBcd(dayOfWeek));
   Wire.send(decToBcd(month));
   Wire.send(decToBcd(year));
   Wire.endTransmission();
}

void getDateDs1307(byte *second,
          byte *minute,
          byte *hour,
          byte *dayOfMonth,
          byte *dayOfWeek,
          byte *month,
          byte *year)
{
  // Reset the register pointer
  Wire.beginTransmission(RTC_I2C_ADDRESS);
  Wire.send(0);
  Wire.endTransmission();

  Wire.requestFrom(RTC_I2C_ADDRESS, 9);

  // A few of these need masks because certain bits are control bits
  *second     = bcdToDec(Wire.receive());
  *second     = bcdToDec(Wire.receive());
  *second     = bcdToDec(Wire.receive() & 0x7f);
  *minute     = bcdToDec(Wire.receive() & 0x7f);
  *hour       = bcdToDec(Wire.receive() & 0x3f);  // Need to change this if 12 hour am/pm
  *dayOfMonth  = bcdToDec(Wire.receive() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.receive() & 0x07);
  *month      = bcdToDec(Wire.receive() & 0x1f);
  *year       = bcdToDec(Wire.receive());
}



// end RTC



void lcd_strobe()
{
      digitalWrite(LCD_EN,HIGH);
      digitalWrite(LCD_EN,LOW);
}

/* write a byte to the LCD in 4 bit mode */

void lcd_write(byte c)
{
      if(c & 0x80) digitalWrite(LCD_D7,HIGH); else  digitalWrite(LCD_D7,LOW);
      if(c & 0x40) digitalWrite(LCD_D6,HIGH); else  digitalWrite(LCD_D6,LOW);
      if(c & 0x20) digitalWrite(LCD_D5,HIGH); else  digitalWrite(LCD_D5,LOW);
      if(c & 0x10) digitalWrite(LCD_D4,HIGH); else  digitalWrite(LCD_D4,LOW);
      lcd_strobe();
      if(c & 0x08) digitalWrite(LCD_D7,HIGH); else  digitalWrite(LCD_D7,LOW);
      if(c & 0x04) digitalWrite(LCD_D6,HIGH); else  digitalWrite(LCD_D6,LOW);
      if(c & 0x02) digitalWrite(LCD_D5,HIGH); else  digitalWrite(LCD_D5,LOW);
      if(c & 0x01) digitalWrite(LCD_D4,HIGH); else  digitalWrite(LCD_D4,LOW);
      lcd_strobe();
      delayMicroseconds(40);
}

/*
 *       Clear and home the LCD
 */

void
lcd_clear(void)
{
      digitalWrite(LCD_RS,LOW);

      lcd_write(0x1);
      delay(2);
}

/* write a string of chars to the LCD */

void
lcd_puts(const char * s)
{
    digitalWrite(LCD_RS,HIGH);      // write characters

      while(*s) lcd_write(*s++);
}

/* write one character to the LCD */

void
lcd_putch(byte c)
{
    digitalWrite(LCD_RS,HIGH); // write characters

      lcd_write(c);
}


/*
 * Go to the specified position
 */

void
lcd_goto(byte pos)
{
    digitalWrite(LCD_RS,0);

      lcd_write(0x80 + pos);
}

/* initialise the LCD - put into 4 bit mode */

void
lcd_init(void)
{
      pinMode(LCD_D7,OUTPUT);
      pinMode(LCD_D6,OUTPUT);
      pinMode(LCD_D5,OUTPUT);
      pinMode(LCD_D4,OUTPUT);
      pinMode(LCD_EN,OUTPUT);
      pinMode(LCD_RS,OUTPUT);

      digitalWrite(LCD_RS, LOW);      // write control bytes

      delay(15);// power on delay

      digitalWrite(LCD_D4, HIGH);      // init!
      digitalWrite(LCD_D5, HIGH); //
      lcd_strobe();
      delay(5);

      lcd_strobe();// init!
      delayMicroseconds(100);

      lcd_strobe();// init!
      delay(5);

      digitalWrite(LCD_D4, LOW);      // set 4 bit mode
      lcd_strobe();
      delayMicroseconds(40);

      lcd_write(0x28);// 4 bit mode, 1/16 duty, 5x8 font, 2lines
      lcd_write(0x0C);// display on
      lcd_write(0x06);// entry mode advance cursor
      lcd_write(0x01);// clear display and reset cursor
}


void setup() {
  lcd_init();
  
   byte second, minute, hour, dayOfMonth, dayOfWeek, month, year;
  Wire.begin();
  Serial.begin(9600);

  // Change these values to what you want to set your clock to.
  // You probably only want to set your clock once and then remove
  // the setDateDs1307 call.
  second = 45;
  minute = 50;
  hour = 10;
  dayOfWeek = 3;
  dayOfMonth = 16;
  month = 12;
  year = 8;
  setDateDs1307(second, minute, hour, dayOfMonth, dayOfWeek, month, year);

}

void loop() {
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
 
  getDateDs1307(&second, &minute, &hour, &dayOfMonth, &dayOfWeek, &month, &year);
 
  Serial.print(hour,DEC);
  Serial.print(":");
  Serial.print(minute,DEC);
  Serial.print(":");
  Serial.print(second,DEC);
  Serial.print("  ");
  Serial.print(dayOfMonth,DEC);
  Serial.print("/");
  Serial.print(month,DEC);
  Serial.print("/");
  Serial.print(year,DEC);
  Serial.print("  Day_of_week:");
  Serial.println(dayOfWeek,DEC);

  lcd_clear();
  lcd_putch(bcdToDec(second));
  delay(1000);

}

I'm not sure you understand what BCD is, or why you'd want to use bcdToDec(), or what you think bcdToDec() would produce.

First, BCD is Binary Coded Decimal. That's a funky bit format for storing numbers. The first decimal digit is in the high four bits of a byte, and the second decimal digit is in the low four bits of the same byte. Only digits from 0 to 9 can be stored, so you can store 00 to 99, but not the other 155 possible byte values (they're invalid for BCD).

If you know binary, then know that in BCD, the decimal value 33 looks like B00110011 (0011 = "3", 0011 = "3"). A regular byte value given decimal 33 would instead look like B00100001 (2^5 + 2^0).

Second, bcdToDec() turns a BCD byte into a regular value. It turns B00110011 into B00100001, so that "3" "3" can be used in regular calculations as 33. Doing regular math on a BCD byte tends to get all screwed up, because the ones place doesn't carry into the tens place properly, like you were taught in grade school.

Lastly, if you print to an ASCII device a number you got from bcdToDec(), you'd be printing the ASCII character associated with that number. If you passed in 33, you'd get the ASCII 33, which is "!". I'm not sure of your whole problem or intent, but I wonder if that's not what you were expecting.

The first decimal digit is in the high four bits of a byte, and the second decimal digit is in the low four bits of the same byte.

Not to be pedantic, but what you've described is more properly called packed BCD. BCD is nothing more than the representation of decimal digits by their binary equivalents.

See the Wikipedia article on BCD for more information.

Not to be argumentative, but if you read that page, you will find that two digits per 8bit byte is typical, and packed BCD refers to the use of the last nibble to do extra encoding such as sign. For example, the old 6502 microprocessor has instructions to handle BCD math two digits at a time (which is customary), it does not have instructions to handle Packed BCD extensions.

Hi halley, thanks for your reply.

Youre right, I didnt really know what BCD meant, I thought it was just another term for binary. But now that I know what bcdToDec() actually does it`s a step in the right direction at least.

The problem is after getting bcd 00110011 33 seconds from the RTC, using bcdToDec() to change it into binary 00100001, and then sending it to the lcd using lcd_putch() it prints "!" as you say. How can I change this so that it says "33" instead? Or should I be changing it to a string somehow and use lcd_puts()?

Sorry if the actual problem wasn`t very clear in the OP, but any more help would be much appreciated.

this should work:

int number;
char buffer[10];
sprintf(buffer, "%d", number);

sprintf works but uses over 2k bytes of program memory, the itoa function uses well under half this amount.

int number;
char buffer[10];
itoa(number, buffer, 10);

Ok I got it to work using itoa which I saw on a different page. I didn`t see your reply before mem but thanks everyone for the help. :smiley:

  char buffer[10];
  itoa(hour, buffer, 10);
  if(hour < 10){lcd_puts("0");}  // 0 space
  lcd_puts(buffer);

Glad you're all set, ezekiel181. Write up a page or at least a posting in the Exhibition area once you get your whole project working. I'm sure it'll be of use to the next hacker.