Thursday, September 20, 2012

Arduino as Serial LCD Display


Project Objective: In this project we will use the Arduino to emulate the PH Anderson #117 serial text display minus big numbers. The layout of a piggyback pcb will complete the  project.

Introduction: See the discussion in the "Controlling a Serial Display" project which covers several of the serial LCD displays on the market. Most of these products have a similar command set and the sketch presented here could be modified to emulate many of these.

Serial LCD displays are available in both text and graphic versions. This project covers text displays with one to four lines and up to 40 columns. Initially, the displays will be limited to 80 total characters, a limitation of the Arduino standard LCD library.

Since the serial LCD is a one wire device, all control of the LCD terminal must use command sequences. Here are the commands used in this project:


Arduino Serial LCD Rev. A: Command Set (as of 9/16/12)
Command 
?a home cursor
?b destructive backspace
?c# set cursor style,   0= none 2= blinking 3=underline
?f clear screen
?g Beep 
?h Backup Cursor (Non-destructive backspace)
?i Forward cursor
?j Up cursor
?k Down cursor
?l Clear cursor line
?m Carriage Return
?n CRLF, cursor at start of next line, line cleared
?p### Position cursor x = ##, y = #
?x##  Position cursor on x column, (two characters are required), first column is column 0, 
?y# Position cursor at y row, first row is row 0 (one character only)
?? display a "?"
?! Send direct command to LCD
?B Backlight Intensity – sets PWM value, two hex digits req. (00 to FF)
?D#      Define custom character. See text.
?# Print a custom character
?H High output on auxiliary digital pins: valid numbers are 1,2,3,4
?L Low output on auxiliary digital pins: valid numbers are 1,2,3,4
?G Configure for LCD geometry. Supported formats: 2X16, 2X20, 2X24, 2X40, 4X16 and 4X20.


Except for the ?p### command they are identical to the PH Anderson, #117 chip. The start character for a command sequence is a '?', but this can be easily changed in the sketch, say to a '!'. The sketch below compiles, but some commands are missing.

One of the nice features of the #117 command set is that control can be accomplished using a PC termial program. In fact, using the Arduino IDE to update the code the built in terminal can be opened to test changes.The piggyback pcb can be modified and updated the same way.

Note: As of 10/08/12 the code was updated, as well as the schematic.The schematic changes were made to simplify the piggyback pcb layout. Because of this there were also changes to code. Several additional commands have been added and we will continue to add more.


Schematic Used with Solderless Breadboard:



The schematic above shows the parts which need to be added to the solderless breadboard for this project. The headers in the schematic are the the headers on the Arduino board. The piggyback board will require a different schematic and include the atmega328, as well as, serial interface circuits and jumpers.

The image below shows the Arduino Serial LCD connected to an Arduino sending serial data at 9600 baude. The text being sent is "Temperature" and "76.8" or "77.6", where the word Temperature is sent once and the numbers sent alternately in the loop section of the sketch. Before the numbers are sent the cursor is positioned at character column 14. The small round object is a piezo speaker.


Arduino on left is sending serial data to Arduino on right.
















Sketch:Version  0.6


/*
Arduino_PHAnderson #117 Serial LCD emulator
Date: 10/08/12
Author: R. LaSalle

*/

#include <LiquidCrystal.h>
#include <EEPROM.h>

/* LCD pin assignments
RS 8
RW 9
E  10
D4 4
D5 5
D6 6
D7 7
*/

#define BP 3
#define BL 11
#define Bd0 14
#define Bd1 15

#define maxrowloc 0
#define maxcolloc 1
#define bootscrloc 2
#define curtypeloc 3
#define ledblloc 4
#define tabloc 5
#define user0 10
#define user1 30
#define user2 50
#define user3 70

#define commchr '?'
//#define VER .5

uint16_t bauderate = 9600;
uint8_t maxrow = 4;
uint8_t maxcol = 20;
uint8_t bootscr = 1;
uint8_t ledbl = 0x7f;
uint8_t nrow =0;
uint8_t ncol =0;
int8_t temp =0;
int8_t temp1 =0;
int8_t temp2 =0;
int8_t temp3 =0;

LiquidCrystal lcd(8,9,10,4,5,6,7);

void setup() {
//  EEPROM.write(maxrowloc,0);
//  EEPROM.write(maxcolloc,0);
  temp=EEPROM.read(maxrowloc);
  temp1=EEPROM.read(maxcolloc);
  if(temp==0 && temp1==0){  //Is EEPROM empty? If yes load defaults.
    maxrow=4;
    maxcol=20;
    EEPROM.write(maxrowloc,maxrow);
    EEPROM.write(maxcolloc,maxcol);
  }
  maxrow=EEPROM.read(maxrowloc);
  maxcol=EEPROM.read(maxcolloc);
  lcd.begin(maxcol,maxrow); // Change this for other screen sizes.
  pinMode(Bd0,INPUT);  //bauderate set bit 0
  pinMode(Bd1,INPUT);  //bauderate set bit 1
  digitalWrite(Bd0,HIGH);  //pullup
  digitalWrite(Bd1,HIGH);  //pullup
  if(digitalRead(Bd0)==LOW && digitalRead(Bd1)==LOW){bauderate=2400;}
  if(digitalRead(Bd0)==HIGH && digitalRead(Bd1)==LOW){bauderate=9600;}
  if(digitalRead(Bd0)==LOW && digitalRead(Bd1)==HIGH){bauderate=19200;}
  if(digitalRead(Bd0)==HIGH && digitalRead(Bd1)==HIGH){bauderate=38400;}
  Serial.begin(bauderate); // Default baudrate.
  pinMode(BL,OUTPUT);
  pinMode(BP,OUTPUT);

  analogWrite(BL, ledbl); // Set maximum brightness.

// Boot Screens
  if(bootscr==1){    //Config screen
    ncol=0;
    nrow=0;
    lcd.setCursor(ncol,nrow);  //home cursor
    lcd.print("Arduino_PHA117");
    lcd.setCursor(0, 1);
    lcd.print("G:");lcd.print(maxrow);lcd.print("x");
    lcd.print(maxcol/10);lcd.print(maxcol%10);
    lcd.print(" B:");lcd.print(bauderate);
    lcd.setCursor(0,0);  //home cursor
  }
}

void loop() {
  byte rxbyte = getc(); // Command
//byte temp; // Parameter

  if (rxbyte == commchr) {
    switch (getc()) {
    case 'p': // Set cursor position (2 parameters, column, row)
 temp=getn();
 temp1=getn();
          temp2=getn();
          if(temp=='e'||temp1=='e'){
            error();
            break;
          }
          temp=temp*10+temp1;
          if(temp>maxcol-1){
            error();
   break;
 }
          ncol=temp;
            if(temp2=='e'){
             error();
            break;
           }
           if(temp2>maxrow-1){
             error();
            break;
           }
            nrow=temp2;                
      lcd.setCursor(ncol, nrow);
      break;
    case 'a': // Cursor home (doesn't clear the screen!)
      lcd.home();
      ncol=0;
      nrow=0;
      break;
case 'b': //Destructive backspace
 lcd.command(16);  //cursor back
 lcd.write(' ');    // write space
 lcd.command(16);  //cursor back
 ncol=ncol-1;
 break;
    case 'c': // Set Cursor type
      switch (getc()){
        case '0':
          lcd.noCursor();
          lcd.noBlink();
          break;
        case '2':
           lcd.cursor();
           lcd.noBlink();
           break;
        case '3':
            lcd.cursor();
            lcd.blink();
            break;
           default:
           break;
        }
        break;
    case 'f': // Clear screen
      lcd.clear();
      ncol=0;
      nrow=0;
      break;
    case 'g': //beep
      tone(BP,4000,250);
      break;
    case 'h': // Move cursor back
      if(ncol>0){
lcd.command(16);
ncol=ncol-1;
      }
      break;
    case 'i': // Move cursor forward
if(ncol<maxcol){
lcd.command(20);
 ncol=ncol+1;
}
      break;
    case 'j': //Cursor up
if(nrow>0){
 nrow=nrow-1;
 lcd.setCursor(ncol,nrow);
}
     break;
     case 'k': //Cursor down
if(nrow<maxrow){
 nrow=nrow+1;
 lcd.setCursor(ncol,nrow);
}
      break;
      case 'l':  //clear cursor line
        ncol=0;
 //       maxcol=20;
        lcd.setCursor(ncol,nrow);
        for(byte i=0;i<=maxcol-1;i++){lcd.write(' ');}
        ncol=0;
        lcd.setCursor(ncol,nrow);
      break;
      case 'm':  //CR
        ncol=0;
        lcd.setCursor(ncol,nrow);
      break;
      case 'n':  //CRLF
        ncol=0;
        if(nrow<maxrow-1){nrow=nrow+1;}
        lcd.setCursor(ncol,nrow);
      break;
      case 'x':
          temp=getn();
          temp1=getn();
          if(temp=='e'||temp1=='e'){
            error();
            break;
          }
          temp=temp*10+temp1;
          if(temp>maxcol-1){
            error();
   break;
 }
          ncol=temp;
          lcd.setCursor(ncol,nrow);
break;

case 'y':
          temp=getn();
           if(temp=='e'){
             error();
            break;
           }
           if(temp>maxrow-1){
             error();
            break;
           }
            nrow=temp;
            lcd.setCursor(ncol,nrow);
        break;
       case 'B':
         temp=geth();
         temp1=geth();
         if(temp==0xff || temp1==0xff){break;}
         temp=temp<<4;
         ledbl=temp+temp1;
         EEPROM.write(ledblloc,ledbl);
         analogWrite(BL,ledbl);
       break;
       case 'G':  //enter R,C
         temp=getn();
         temp1=getn();
         temp2=getn();
         if(temp=='e' || temp1=='e' || temp2=='e'){error();break;}
         maxcol=temp1*10+temp2;
         maxrow=temp;
         EEPROM.write(maxcolloc,maxcol);
         EEPROM.write(maxrowloc,maxrow);
       break;
    }
  }
  else{
  // Otherwise its a plain char so we print it to the LCD.
  lcd.write(rxbyte);
  ncol=ncol+1;
  }
}

/*
 * Waits for a byte to be available, reads and returns it.
 */
byte getc() {  //get character
  while (Serial.available() == 0);
  return Serial.read();
}

byte getn() {  //get decimal digit
  byte temp;
  while (Serial.available() ==0);
  temp=Serial.read()-48;
  if(0>temp>9){
    return 'e';
  }
  else{
  return temp;
  }
}

byte geth(){  //get hex digit
  byte temp=getc();
  if(temp>=0x30 && temp<=0x39){
    temp=temp-0x30;
    return temp;
  }
  if(temp>=0x61 && temp<=0x66){
    temp=temp-0x57;
    return temp;
  }
  return 0xff;
}

void error(){
  tone(6,4000,250);
  return;
}



.

To Be Continued