I've included the code below. It's a bit of a hack but it does work and I use it on my Media PC. If you find any bugs 9and i'm sure there are some) or make any mods please let me know.
Code: Select all
/*
serial_lcd_4x40_smartie - an ethernet or serial based LCD Smartie sketch
Note that the ethernet code has been ommitted from this version
Based on a simple HD44780 based LCD Display driver
Lots of code copied from the LiquidCrystal library
Dave Perrow January 2014
Connections:
* On UNO (these pins used so that the ethernet shield and the LCD can co-exist and are my choice and cable colours of a ribbon)
* LCD RW(GREEN) is connected to gnd, RS (BLUE) is on pin 19(A5), EN (Enable)(YELLOW) on 18(A4),
* D4 (ORANGE)on 17 (A03), D5 (RED) on 16(A2), D6 (BROWN)on 15(A1), D7 (BLACK) on 14(A0)
* LCD CONTRAST (V0 OR VEE)(PURPLE) CONNECTED TO PIN 5 (PWM) WITH 100uF cap between pin 5 and gnd or not
* VSS (WHITE)to gnd, VDD(VCC)(GREY) to +5v, L+ (WHITE)to +Vin (**** only if usb powered)
* LCD (L-)(GREY) Backlight on pin 6 (PWM) via transistor amp (BC639) or not
*
* Note this only works with a USB power source because i'm using Vin to the backlight.
* It'll probably blow up if you use an external power source.
*
* JMD 162A
LCM Color Signal Arduino
1 White VSS Gnd
2 Grey VCC +5v
3 Purple VEE D5
4 Blue RS A5
5 Green R/W Gnd
6 Yellow E A4
7 nc DB0 nc
8 nc DB1 nc
9 nc DB2 nc
10 nc DB3 nc
11 Orange DB4 A3
12 Red DB5 A2
13 Brown DB6 A1
14 Black DB7 A0
15 White LED+ Vin
16 Grey LED- D6
40x4 Signal Color Arduino
1 DB7 Black A3
2 DB6 Brown A2
3 DB5 Red A1
4 DB4 Orange A0
5 DB3 nc nc
6 DB2 nc nc
7 DB1 nc nc
8 DB0 nc nc
9 E1 Yellow A4
10 R/W Green Gnd
11 RS Blue A5
12 V0 Purple D5
13 VSS White Gnd
14 VDD Grey +5v
15 E2 purple D7
16 nc nc nc
17 LED+ White Vin
18 LED- Grey D6
*/
#include <Arduino.h>
#define VERSION "1.01"
//---------------------------------------------------------------------------------------------
// LCDdetailed stuff
//---------------------------------------------------------------------------------------------
// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80
// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00
// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00
// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00
// flags for function set
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00
//---------------------------------------------------------------------------------------------
// LCD & LED pin configs
//---------------------------------------------------------------------------------------------
#define LCDPORT PORTC
#define LCDDDR DDRC
#define LCDBITS B00111111
#define DATAMASK B11110000
#define RSBITS B00100000
#define RSMASK B11011111
#define ENBITS B00010000
#define ENMASK B11101111
#define E2 7 // Enable pin for bottom 2 lines of display
#define backlight_pin 6 // LCD Backlight pin (PWM)
#define contrast_pin 5 // LCD Contrast pin (PWM)
#define led13 13 // Pin 13 has an LED connected on most Arduino boards. So give it a name:
#define GPO1 12 // GPO pin
#define DISPLAY_WIDTH 40
#define DISPLAY_HEIGHT 4
// Mapping table maps incoming characters - this needs work and verifying
unsigned char cmap[] = {
0x67, 0xE7, 0x6A, 0xEA, 0x70, 0xF0, 0x71, 0xF1, 0x79, 0xF9,
0xE4, 0xE1, 0xF1, 0xEE, 0xF6, 0xEF, 0xFC, 0xF5,
//accented -> plain equivalent and misc symbol translation
0xA3, 0xED, 0xB0, 0xDF, 0xB5, 0xE4, 0xC0, 0x41, 0xC1, 0x41,
0xC2, 0x41, 0xC3, 0x41, 0xC4, 0x41, 0xC5, 0x41, 0xC8, 0x45,
0xC9, 0x45, 0xCA, 0x45, 0xCB, 0x45, 0xCC, 0x49, 0xCD, 0x49,
0xCE, 0x49, 0xCF, 0x49, 0xD1, 0x43, 0xD2, 0x4F, 0xD3, 0x4F,
0xD4, 0x4F, 0xD5, 0x4F, 0xD6, 0x4F, 0xD8, 0x4F, 0xD9, 0x55,
0xDA, 0x55, 0xDB, 0x55, 0xDC, 0x55, 0xDD, 0x59, 0xDF, 0xE2,
0xE0, 0x61, 0xE1, 0x61, 0xE2, 0x61, 0xE3, 0x61, 0xE5, 0x61,
0xE7, 0x63, 0xE8, 0x65, 0xE9, 0x65, 0xEA, 0x65, 0xEB, 0x65,
0xEC, 0x69, 0xED, 0x69, 0xEE, 0x69, 0xEF, 0x69, 0xF2, 0x6F,
0xF3, 0x6F, 0xF4, 0x6F, 0xF5, 0x6F, 0xF8, 0x6F, 0xF7, 0xFD,
0xF9, 0x75, 0xFA, 0x75, 0xFB, 0x75
};
uint8_t _displayfunction;
uint8_t _displaycontrol;
uint8_t _displaymode;
uint8_t _initialized;
uint8_t _numlines;
uint8_t _currline;
uint8_t _currcol;
uint8_t _epin;
//=============================================================================================
// setup code called at power up or reset
//=============================================================================================
void setup()
{ int i;
// Set up serial devices
Serial.begin(9600); // Open serial communications for debug messages
pinMode(contrast_pin, OUTPUT); // Set the contrast pin to an output
analogWrite(contrast_pin, 0); // set the contrast value (0-255) from the variable (0-100)
pinMode(backlight_pin, OUTPUT); // Set the backlight pin to an output
analogWrite(backlight_pin, 0); // Set the backlight value (0-255) from the variable (0-100)
// ---------------- Set up the LED's and other outputs --------------------
pinMode(led13,OUTPUT); // initialize the digital pin for the on-board led as an output.
pinMode(GPO1, OUTPUT); // initialize the digital pin for the on-board led as an output.
// ---------------- Set up the LCD ---------------------------------------
LCD_init(); // set up the LCD
LCD_setCursor(23,1);
LCD_print("**LCD SMARTIE**"); // Print a message to the LCD
LCD_setCursor(22,2);
LCD_print("on Arduino Uno v"); // Print a message to the LCD
LCD_print(VERSION); // Print a message to the LCD
}
//=============================================================================================
// Main program continually called till power off
// Check for commands from the serial port
//=============================================================================================
void loop()
{ if (Serial.available()>0) { digitalWrite(led13,1); emulate(Serial.read()); digitalWrite(led13,0); } }
//=======================================================================================================
// routine to wait and get the next character
//=======================================================================================================
byte nextb()
{ while (Serial.available()==0) delay(2); // Wait for the next character
return (byte) (Serial.read() & 0xff); // read the incoming byte
}
//=======================================================================================================
// routine to emulate the matrix orbital commands
//=======================================================================================================
void emulate(uint8_t c)
{ uint8_t rxbyte=c;
uint8_t i;
uint8_t chars[8];
uint8_t num;
#define CASE break; case
if (rxbyte == 0xFE) // Matrix Orbital uses 254 prefix for commands
{ while (rxbyte == 0xFE) { rxbyte = nextb(); } // ignore multiple 0xFE's
switch (rxbyte)
{ case 0: // ignore nulls
CASE 0x23: // Read serial number (or place large number)
CASE 0X24: // Read version number
CASE 0X26: // Poll key presses
CASE 0X33: // Change I2C slave address (1 parameter, address)
CASE 0X36: Serial.print(VERSION); // Read version number
CASE 0X37: Serial.print(9); // Read module type (9 for LK204-25)
CASE 0X39: // Change baud rate (1 parameter, baud rate)
CASE 0X3B: // Exit flow-control mode
CASE 0X3D: nextb(); nextb(); // Place vertical bar (2 parameters, column, length)
CASE 0X40: // Change the startup screen (followed by 32 or whatever chars)
CASE 0X41: // Auto transmit keypresses on
CASE 0x42: nextb(); // ignore time character
analogWrite(backlight_pin, 180); // Set the backlight on (at previously set brightness)
CASE 0X43: // Auto line wrap on
CASE 0X44: // Auto line wrap off
CASE 0X45: // Clear key buffer
CASE 0x46: analogWrite(backlight_pin, 255); // Set the backlight off
CASE 0x47: LCD_setCursor(nextb()-1,nextb()-1); // set cursor position
CASE 0x48: LCD_home(); // cursor home (reset display position)
CASE 0x4A: LCD_cursor(); // show underline cursor
CASE 0x4B: LCD_noCursor(); // underline cursor off
CASE 0x4C: LCD_command(16); // move cursor left
CASE 0x4D: LCD_command(20); // move cursor right
CASE 0x4E: num=nextb(); // define custom char num
for (i=0; i<8; i++) chars[i]=nextb(); // Bit patterns
LCD_createChar(num,chars);
CASE 0X4F: // Auto transmit keypress off
CASE 0x50: analogWrite(contrast_pin, 256-nextb()); // Set the contrast value (0-255) from the variable (0-100)
CASE 0X51: LCD_autoscroll(); // Auto scroll on
CASE 0X52: LCD_noAutoscroll(); // Auto scroll off
CASE 0x53: LCD_blink(); // show blinking block cursor
CASE 0x54: LCD_noBlink(); // hide blinking block cursor
CASE 0X55: // Set debounce time (1 paramater, time)
CASE 0X56: if (nextb() == 1) digitalWrite(GPO1, LOW); // General Purpose Output off (1 parameter, number)
CASE 0X57: if (nextb() == 0) digitalWrite(GPO1, HIGH); // General Purpose Output on (1 parameter, number)
CASE 0x58: LCD_clear(); // clear display, cursor home
CASE 0x5B: LCD_noCursor(); // cursor off
CASE 0X60: // Auto repeat mode off
CASE 0X68: // Initialize horizontal bar
CASE 0X6D: // Initialize medium number
CASE 0X6E: // Initialize lange numbers
CASE 0X6F: // Place medium numbers
CASE 0X73: // Initialize narrow vertical bar
CASE 0X76: // Initialize wide vertical bar
CASE 0X7C: // Place horizontal bar graph (4 parameters, column, row, direction, length)
CASE 0X7E: // Set auto repeat mode (1 parameter, mode)
CASE 0x91: analogWrite(contrast_pin, 256-nextb()); // Set the contrast value (0-255) and remember from the variable (0-100)
CASE 0x98: analogWrite(backlight_pin, nextb()); // Set the backlight value (0-255)and remember (doesn't save value, though)
CASE 0x99: analogWrite(backlight_pin, nextb()); // Set the backlight value (0-255) from the variable (0-100)
CASE 0XA0: // Transmission protocol select (1 parameter, protocol)
CASE 0XA4: // Setting a non-standart baudrate (1 parameter, speed)
CASE 0XC0: // Load custom characters (1 parameter, bank)
CASE 0XC1: // Save custom character (3 parameters, bank, id, data)
CASE 0XC2: // Save startup screen custom characters (2 parameters, id, data)
CASE 0XC3: // Set startup GPO state (2 parameters, number, state)
CASE 0XC8: switch (nextb()) // Dallas 1-Wire
{ case 1: // Dallas 1-Wire transaction (6 parameters, flags, send, bits, receive, bits, data)
case 2: // Search for a 1-Wire device
default: break;
}
CASE 0XD5: break; // Assign keypad codes
default: break; // all other commands ignored and parameter byte discarded
}
}
else if ((rxbyte!='\n') && (rxbyte!=0)) // ignore newline and null characters
{ // do any character mapping required.
for (i=0; i<(sizeof(cmap)/2); i++)
{ if (rxbyte==cmap[i*2]) { rxbyte=cmap[i*2+1]; break; } } // if we find it change it
LCD_write((char)rxbyte); // print the character on the lcd
}
}
// When the display powers up, it is configured as follows:
//
// 1. Display clear
// 2. Function set:
// DL = 1; 8-bit interface data
// N = 0; 1-line display
// F = 0; 5x8 dot character font
// 3. Display on/off control:
// D = 0; Display off
// C = 0; Cursor off
// B = 0; Blinking off
// 4. Entry mode set:
// I/D = 1; Increment by 1
// S = 0; No shift
//
// Note, however, that resetting the Arduino doesn't reset the LCD, so we
// can't assume that its in that state when a sketch starts.
//
void LCD_init(void)
{
LCDDDR = LCDDDR | LCDBITS; // set all bits as outputs
_displayfunction = LCD_4BITMODE | LCD_5x8DOTS;
_epin=0;
pinMode(E2, OUTPUT);
digitalWrite(E2, LOW);
_displayfunction |= LCD_2LINE;
_numlines = 4;
_currline = 0; _currcol=0;
// 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
delayMicroseconds(50000);
// Now we pull both RS and R/W low to begin commands
LCDPORT = LCDPORT & RSMASK; // digitalWrite(_rs_pin, LOW);
LCDPORT = LCDPORT & ENMASK; // digitalWrite(_enable_pin, LOW);
// put the LCD into 4 bit mode according to the hitachi HD44780 datasheet
// figure 24, pg 46 we start in 8bit mode, try to set 4 bit mode
for (uint8_t i=0; i<2; i++)
{ _epin=i;
LCD_write4bits(0x03);
delayMicroseconds(4500); // wait min 4.1ms
LCD_write4bits(0x03); // second try
delayMicroseconds(4500); // wait min 4.1ms
LCD_write4bits(0x03); // third go!
delayMicroseconds(150);
LCD_write4bits(0x02); // finally, set to 4-bit interface
LCD_command(LCD_FUNCTIONSET | _displayfunction); // finally, set # lines, font size, etc.
// turn the display on with no cursor or blinking default
_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
LCD_display();
LCD_clear(); // clear it off
// Initialize to default text direction (for romance languages)
_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
LCD_command(LCD_ENTRYMODESET | _displaymode); // set the entry mode
}
_epin=0;
}
/********** high level commands, for the user! */
// Clear the display
void LCD_clear()
{ _epin=0; LCD_command(LCD_CLEARDISPLAY); delayMicroseconds(2000); // clear display, set cursor position to zero. note this command takes a long time!
_epin=1; LCD_command(LCD_CLEARDISPLAY); delayMicroseconds(2000); // clear display, set cursor position to zero. note this command takes a long time!
_epin=0; _currline=0; _currcol=0;
}
// Home
void LCD_home()
{ _epin=0; LCD_command(LCD_RETURNHOME); delayMicroseconds(2000); // set cursor position to zero. note this command takes a long time!
_currline=0; _currcol=0;
}
// Set cursor position
void LCD_setCursor(uint8_t col, uint8_t row)
{ int row_offsets[] = { 0, 0x40, 0, 0x40 };
if ( row >= _numlines ) row = _numlines-1; // we count rows starting w/0
_epin=0; if (row>1) _epin=1;
LCD_command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
_currline=row; _currcol=col;
}
void LCD_command_both(uint8_t value) {uint8_t e=_epin; for (_epin=0; _epin<2; _epin++) LCD_command(value); _epin=e; } // Send command to both chips
void LCD_noDisplay() { _displaycontrol &= ~LCD_DISPLAYON; LCD_command_both(LCD_DISPLAYCONTROL | _displaycontrol); } // Turn the display off (quickly)
void LCD_display() { _displaycontrol |= LCD_DISPLAYON; LCD_command_both(LCD_DISPLAYCONTROL | _displaycontrol); } // Turn the display on (quickly)
void LCD_noCursor() { _displaycontrol &= ~LCD_CURSORON; LCD_command_both(LCD_DISPLAYCONTROL | _displaycontrol); } // Turns the underline cursor off
void LCD_cursor() { _displaycontrol |= LCD_CURSORON; LCD_command_both(LCD_DISPLAYCONTROL | _displaycontrol); } // Turns the underline cursor on
void LCD_noBlink() { _displaycontrol &= ~LCD_BLINKON; LCD_command_both(LCD_DISPLAYCONTROL | _displaycontrol); } // Turn off the blinking cursor
void LCD_blink() { _displaycontrol |= LCD_BLINKON; LCD_command_both(LCD_DISPLAYCONTROL | _displaycontrol); } // Turn on the blinking cursor
void LCD_scrollDisplayLeft(void) { LCD_command_both(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); } // Scroll the display left without changing the RAM
void LCD_scrollDisplayRight(void) { LCD_command_both(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); } // Scroll the display right without changing the RAM
void LCD_leftToRight(void) { _displaymode |= LCD_ENTRYLEFT; LCD_command_both(LCD_ENTRYMODESET | _displaymode); } // This is for text that flows Left to Right
void LCD_rightToLeft(void) { _displaymode &= ~LCD_ENTRYLEFT; LCD_command_both(LCD_ENTRYMODESET | _displaymode); } // This is for text that flows Right to Left
void LCD_autoscroll(void) { _displaymode |= LCD_ENTRYSHIFTINCREMENT; LCD_command_both(LCD_ENTRYMODESET | _displaymode); } // This will 'right justify' text from the cursor
void LCD_noAutoscroll(void) { _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; LCD_command_both(LCD_ENTRYMODESET | _displaymode); } // This will 'left justify' text from the cursor
// This allows us to fill the first 8 CGRAM locations with custom characters
void LCD_createChar(uint8_t location, uint8_t charmap[])
{ uint8_t e=_epin;
for (_epin=0; _epin<2; _epin++) { LCD_command(LCD_SETCGRAMADDR | ((location &0x7) << 3)); for (int i=0; i<8; i++) LCD_write(charmap[i]); }
_epin=e;
}
/*********** mid level commands, for sending data/cmds */
inline void LCD_command(uint8_t value) { LCD_send(value, LOW); }
inline size_t LCD_write(uint8_t value) { LCD_send(value, HIGH); return 1; } // assume sucess
void LCD_print(char *s)
{ uint8_t lc=_currline;
for (uint8_t i=0; s[i]!=0; i++)
{ LCD_write((uint8_t)s[i]);
_currcol++;
if (_currcol>39)
{ _currcol=0; _currline++;
if (_currline>=_numlines) _currline=0;
{ if (_currline>1) _epin=1; else _epin=0;
if (lc!=_currline) LCD_setCursor(_currcol,_currline);
}
}
}
}
/************ low level data pushing commands **********/
// write either command or data, with automatic 4/8-bit selection
void LCD_send(uint8_t value, uint8_t mode)
{ if (mode==0) LCDPORT = LCDPORT & RSMASK; else LCDPORT = LCDPORT | RSBITS;
LCD_write4bits(value>>4);
LCD_write4bits(value);
}
void LCD_write4bits(uint8_t value)
{ LCDPORT = LCDPORT & DATAMASK;
LCDPORT = LCDPORT | (value & 0x0F);
delayMicroseconds(1);
if (_epin==0)
{ LCDPORT = LCDPORT & ENMASK; delayMicroseconds(1); // digitalWrite(_enable_pin, LOW);
LCDPORT = LCDPORT | ENBITS; delayMicroseconds(1); // digitalWrite(_enable_pin, HIGH); // enable pulse must be >450ns
LCDPORT = LCDPORT & ENMASK; delayMicroseconds(200); // digitalWrite(_enable_pin, LOW); // commands need > 37us to settle
}
else
{ digitalWrite(E2,LOW); delayMicroseconds(1); // digitalWrite(_enable_pin, LOW);
digitalWrite(E2,HIGH); delayMicroseconds(1); // digitalWrite(_enable_pin, HIGH); // enable pulse must be >450ns
digitalWrite(E2,LOW); delayMicroseconds(200); // digitalWrite(_enable_pin, LOW); // commands need > 37us to settle
}
}