├── 4x20-LCD-panel.jpg ├── I2C_connector.jpg ├── LCD1.jpg ├── LCD2.jpg ├── LCDback.jpg ├── README.md ├── YwRobotLCD-CU-450.jpg ├── joystick.jpg └── src └── org └── raiderrobotics └── i2c.java /4x20-LCD-panel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaiderRobotics/FRC-LCD-Display/50269ff7c74fae049c91f410d71494c664e0f29e/4x20-LCD-panel.jpg -------------------------------------------------------------------------------- /I2C_connector.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaiderRobotics/FRC-LCD-Display/50269ff7c74fae049c91f410d71494c664e0f29e/I2C_connector.jpg -------------------------------------------------------------------------------- /LCD1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaiderRobotics/FRC-LCD-Display/50269ff7c74fae049c91f410d71494c664e0f29e/LCD1.jpg -------------------------------------------------------------------------------- /LCD2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaiderRobotics/FRC-LCD-Display/50269ff7c74fae049c91f410d71494c664e0f29e/LCD2.jpg -------------------------------------------------------------------------------- /LCDback.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaiderRobotics/FRC-LCD-Display/50269ff7c74fae049c91f410d71494c664e0f29e/LCDback.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FRC-LCD-Display 2 | 3 | :large_blue_diamond: The source code here demonstrates how to set up a 4x20 LCD panel display with the Roborio (for FRC First Robotics Competition). 4 | 5 | All you have to do to use it is add the code that I've written, and then run the command `LCDwriteString(String s, int line);` where String s is the string to write to the display, and int line is line number (1-4). 6 | 7 | ![p1](https://raw.githubusercontent.com/RaiderRobotics/FRC-LCD-Display/master/LCD1.jpg) 8 | ![p2](https://raw.githubusercontent.com/RaiderRobotics/FRC-LCD-Display/master/joystick.jpg) 9 | ![p3](https://raw.githubusercontent.com/RaiderRobotics/FRC-LCD-Display/master/LCD2.jpg) 10 | 11 | ------------------- 12 | ## First the hardware: 13 | 14 | :black_small_square: We're using a display like the one in the photo 15 | 16 | ![photo1](https://github.com/RaiderRobotics/FRC-LCD-Display/blob/master/4x20-LCD-panel.jpg). 17 | 18 | ![p5](https://raw.githubusercontent.com/RaiderRobotics/FRC-LCD-Display/master/LCDback.jpg) 19 | 20 | :black_small_square: Note that it comes with a backplate thing mounted on it. This backplate already has all of the connections between the LCD display and the I2C protocol. 21 | 22 | ![photo2](https://github.com/RaiderRobotics/FRC-LCD-Display/blob/master/YwRobotLCD-CU-450.jpg) 23 | 24 | :black_small_square: The connectors on the back of the LCD display do not match the connectors on the RoboRIO. The RoboRIO has the pins in a different order. Also note that the LCD display requires 5V, not the 3.3V on the RoboRIO I2C connector. We're just using 5V from a digital IO pin. You could also use the 5V from the Voltage Regulator Module. 25 | 26 | 27 | 28 | 29 | ------------------------------- 30 | 31 | ## CODE 32 | 33 | ### Background information 34 | 35 | :boom: **WPI Documentation** The [WPI documenation](http://first.wpi.edu/FRC/roborio/stable/docs/java/classedu_1_1wpi_1_1first_1_1wpilibj_1_1I2C.html) is completely useless. The [source code](https://usfirst.collab.net/gerrit/gitweb?p=allwpilib.git;f=wpilibj/wpilibjava/src/main/java/edu/wpi/first/wpilibj/I2C.java;h=8476) is no help either. 36 | 37 | It's easy to make an I2C object: `lcdDisplay = new I2C(I2C.Port.kOnboard, 0x27);` The address of most LCD panels is 0x27. It's a lot harder to write to the display since the only thing that the documentation tells you is `boolean write (int registerAddress, int data)`. There is no explanation of what the register address should be, no examples of this being used anywhere. *(I finally figured it out, see below.)* 38 | 39 | :boom: LCD Panels are all driven by the [Hitachi HD44780 driver](http://www.waveshare.com/datasheet/LCD_en_PDF/HD44780.pdf). This page provides details, explains the display addressing, and lists the commands that are build in. 40 | 41 | :boom: There are some very useful code examples here: 42 | 43 | * LCD panel display documentation: http://www.microcontrollerboard.com/lcd.html 44 | * Using Java to access I2C devices with examples of LCD display: https://docs.oracle.com/javame/8.1/me-dev-guide/i2c.htm 45 | * This helped understand the hardware a bit. http://letsmakerobots.com/content/drive-standard-hd44780-lcd-using-pcf8574-and-i2c 46 | 47 | :boom: Finally, I realized that [my python code](https://github.com/salamander2/RaspberryPi/tree/master/programs/LCD) :snake: (which I got from someone else's Raspberry Pi repository) could be directly ported to Java. **It worked!!!** 48 | 49 | ----------------- 50 | 51 | ### How the code works 52 | 53 | * It turns out that the register to write the I2C data is ALWAYS 0 (I'm talking about the WPI `I2C.write()` command). Why? This is either the I2C controller or the register on the I2C device. So it seems that the LCD display only has one (external facing) register. 54 | * However ... internally, there are TWO registers: a command register and a data/display register. The registers are INTERNAL and are selected by using the Rs bit or not. 55 | * Commands are always written 4 bits at a time. Why? This is because it's the way that everyone does it. (Originally it was so that you need fewer data lines connected to your display.) 56 | * Commands have to be "strobed" using the En bit for them to take effect. 57 | * Commands do not execute instantaneously, so you need a short delay between them. 58 | * There is a sequence of initialization commands that must be done to set up the display after it's been powered on before it can be used. 59 | * Many of the other commands (cursor movement, etc.) I have not tried. 60 | 61 | -------------------------------------------------------------------------------- /YwRobotLCD-CU-450.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaiderRobotics/FRC-LCD-Display/50269ff7c74fae049c91f410d71494c664e0f29e/YwRobotLCD-CU-450.jpg -------------------------------------------------------------------------------- /joystick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaiderRobotics/FRC-LCD-Display/50269ff7c74fae049c91f410d71494c664e0f29e/joystick.jpg -------------------------------------------------------------------------------- /src/org/raiderrobotics/i2c.java: -------------------------------------------------------------------------------- 1 | package org.raiderrobotics; 2 | 3 | import edu.wpi.first.wpilibj.*; 4 | 5 | /* 6 | * LCD display panel code written by Michael Harwood, FRC Team 5024. November 2015. 7 | * Please see https://github.com/RaiderRobotics/FRC-LCD-Display for source code repository and complete documentation. 8 | * 9 | * This java code is not originally mine. 10 | * It is based on my python code which in turn came from Paul Barber: https://github.com/paulbarber/raspi-gpio 11 | * 12 | * Feel free to copy it and modify it as desired 13 | */ 14 | 15 | public class Robot extends IterativeRobot { 16 | 17 | /* ********************************************************** 18 | * Constants for LCD Panel 19 | * ********************************************************/ 20 | // LCD Commands 21 | private static final int LCD_CLEARDISPLAY = 0x01; 22 | private static final int LCD_RETURNHOME = 0x02; 23 | private static final int LCD_ENTRYMODESET = 0x04; 24 | private static final int LCD_DISPLAYCONTROL = 0x08; 25 | private static final int LCD_CURSORSHIFT = 0x10; 26 | private static final int LCD_FUNCTIONSET = 0x20; 27 | private static final int LCD_SETCGRAMADDR = 0x40; 28 | private static final int LCD_SETDDRAMADDR = 0x80; 29 | 30 | // Flags for display on/off control 31 | private static final int LCD_DISPLAYON = 0x04; 32 | private static final int LCD_DISPLAYOFF = 0x00; 33 | private static final int LCD_CURSORON = 0x02; 34 | private static final int LCD_CURSOROFF = 0x00; 35 | private static final int LCD_BLINKON = 0x01; 36 | private static final int LCD_BLINKOFF = 0x00; 37 | 38 | // Flags for display entry mode 39 | // private static final int LCD_ENTRYRIGHT = 0x00; 40 | private static final int LCD_ENTRYLEFT = 0x02; 41 | private static final int LCD_ENTRYSHIFTINCREMENT = 0x01; 42 | private static final int LCD_ENTRYSHIFTDECREMENT = 0x00; 43 | 44 | // Flags for display/cursor shift 45 | private static final int LCD_DISPLAYMOVE = 0x08; 46 | private static final int LCD_CURSORMOVE = 0x00; 47 | private static final int LCD_MOVERIGHT = 0x04; 48 | private static final int LCD_MOVELEFT = 0x00; 49 | 50 | // flags for function set 51 | private static final int LCD_8BITMODE = 0x10; 52 | private static final int LCD_4BITMODE = 0x00; 53 | private static final int LCD_2LINE = 0x08; //for 2 or 4 lines actually 54 | private static final int LCD_1LINE = 0x00; 55 | private static final int LCD_5x10DOTS = 0x04; //seldom used!! 56 | private static final int LCD_5x8DOTS = 0x00; 57 | 58 | // flags for backlight control 59 | private static final int LCD_BACKLIGHT = 0x08; 60 | private static final int LCD_NOBACKLIGHT = 0x00; 61 | 62 | //bitmasks for register control 63 | private static final int En = 0b00000100; // Enable bit 64 | private static final int Rw = 0b00000010; // Read/Write bit 65 | private static final int Rs = 0b00000001; // Register select bit 66 | 67 | /* ********************************************************************************* 68 | * End of LCD constants 69 | * ********************************************************************************/ 70 | 71 | final static double MAXSPEED = 0.50; 72 | 73 | // instance variables 74 | RobotDrive driveTrain; 75 | Joystick stick1; 76 | Talon talon1, talon2; 77 | I2C lcdDisplay; 78 | 79 | long startTime = 0; 80 | 81 | public void robotInit() { 82 | //first to run, only runs once 83 | talon1 = new Talon(1); 84 | talon2 = new Talon(2); 85 | driveTrain = new RobotDrive(talon1, talon2); 86 | stick1 = new Joystick(0); 87 | //invert motors 88 | driveTrain.setInvertedMotor(RobotDrive.MotorType.kFrontLeft, true); 89 | driveTrain.setInvertedMotor(RobotDrive.MotorType.kFrontRight, true); 90 | driveTrain.setInvertedMotor(RobotDrive.MotorType.kRearLeft, true); 91 | driveTrain.setInvertedMotor(RobotDrive.MotorType.kRearRight, true); 92 | 93 | initLCD(); 94 | } 95 | 96 | /* *************************************************************************** 97 | * Methods for using LCD Display 98 | * **************************************************************************/ 99 | 100 | void initLCD() { 101 | lcdDisplay = new I2C(I2C.Port.kOnboard, 0x27); 102 | 103 | LCDwriteCMD(0x03); 104 | LCDwriteCMD(0x03); 105 | LCDwriteCMD(0x03); 106 | LCDwriteCMD(0x02); 107 | //4 bit mode??? -- yes. Always. It's the default way of doing this for LCD displays 108 | LCDwriteCMD( LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE ); 109 | LCDwriteCMD( LCD_DISPLAYCONTROL | LCD_DISPLAYON ); 110 | LCDwriteCMD( LCD_CLEARDISPLAY ); 111 | LCDwriteCMD( LCD_ENTRYMODESET | LCD_ENTRYLEFT ); 112 | zsleep(10); 113 | } 114 | 115 | //write a sleep method to get rid of the try-catch stuff 116 | void zsleep(int t) { 117 | try { Thread.sleep(t); 118 | } catch(InterruptedException e) {} 119 | } 120 | 121 | //This is for writing commands, 4 bits at a time 122 | void LCDwriteCMD (int data) { 123 | LCD_rawWrite(data & 0xF0); 124 | LCD_rawWrite((data <<4 ) & 0xF0); 125 | } 126 | 127 | //This is for writing a character, 4 bits at a time 128 | void LCDwriteChar ( int data) { 129 | LCD_rawWrite( Rs | (data & 0xF0)); 130 | LCD_rawWrite( Rs | ((data <<4 ) & 0xF0)); 131 | } 132 | 133 | void LCD_rawWrite( int data) { 134 | lcdDisplay.write(0, data | LCD_BACKLIGHT ); 135 | strobe(data); 136 | } 137 | 138 | void strobe(int data){ 139 | // Syntax: lcdDisplay.write(reg,data); 140 | lcdDisplay.write(0, data | En | LCD_BACKLIGHT ); 141 | zsleep(1); 142 | lcdDisplay.write(0, (data & ~En) | LCD_BACKLIGHT ); 143 | zsleep(1); 144 | } 145 | 146 | //This is the "public" method. The one that is actually used by other code to write to the display. 147 | void LCDwriteString(String s, int line) { 148 | switch (line) { 149 | case 1: LCDwriteCMD(0x80); break; 150 | case 2: LCDwriteCMD(0xC0); break; 151 | case 3: LCDwriteCMD(0x94); break; 152 | case 4: LCDwriteCMD(0xD4); break; 153 | default: return; //invalid line number does nothing. 154 | } 155 | 156 | //limit to 20 chars/line so we don't have to worry about overflow messing up the display 157 | if (s.length() > 20) s = s.substring(0, 20); 158 | 159 | for (int i=0; i