PageRenderTime 53ms CodeModel.GetById 14ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 0ms

/firmware/src/shared/LiquidCrystal.cc

http://github.com/makerbot/G3Firmware
C++ | 343 lines | 227 code | 60 blank | 56 comment | 25 complexity | 7e68b8169f0c423623739dd6498b1d87 MD5 | raw file
  1#include "LiquidCrystal.hh"
  2
  3#include <stdio.h>
  4#include <string.h>
  5#include <util/delay.h>
  6
  7// When the display powers up, it is configured as follows:
  8//
  9// 1. Display clear
 10// 2. Function set:
 11//    DL = 1; 8-bit interface data
 12//    N = 0; 1-line display
 13//    F = 0; 5x8 dot character font
 14// 3. Display on/off control:
 15//    D = 0; Display off
 16//    C = 0; Cursor off
 17//    B = 0; Blinking off
 18// 4. Entry mode set:
 19//    I/D = 1; Increment by 1
 20//    S = 0; No shift
 21//
 22// Note, however, that resetting the Arduino doesn't reset the LCD, so we
 23// can't assume that its in that state when a sketch starts (and the
 24// LiquidCrystal constructor is called).
 25
 26LiquidCrystal::LiquidCrystal(Pin rs, Pin rw, Pin enable,
 27			     Pin d0, Pin d1, Pin d2, Pin d3,
 28			     Pin d4, Pin d5, Pin d6, Pin d7)
 29{
 30  init(0, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7);
 31}
 32
 33LiquidCrystal::LiquidCrystal(Pin rs, Pin enable,
 34			     Pin d0, Pin d1, Pin d2, Pin d3,
 35			     Pin d4, Pin d5, Pin d6, Pin d7)
 36{
 37  init(0, rs, Pin(), enable, d0, d1, d2, d3, d4, d5, d6, d7);
 38}
 39
 40LiquidCrystal::LiquidCrystal(Pin rs, Pin rw, Pin enable,
 41			     Pin d0, Pin d1, Pin d2, Pin d3)
 42{
 43  init(1, rs, rw, enable, d0, d1, d2, d3, Pin(), Pin(), Pin(), Pin());
 44}
 45
 46LiquidCrystal::LiquidCrystal(Pin rs,  Pin enable,
 47			     Pin d0, Pin d1, Pin d2, Pin d3)
 48{
 49  init(1, rs, Pin(), enable, d0, d1, d2, d3, Pin(), Pin(), Pin(), Pin());
 50}
 51
 52void LiquidCrystal::init(uint8_t fourbitmode, Pin rs, Pin rw, Pin enable,
 53			 Pin d0, Pin d1, Pin d2, Pin d3,
 54			 Pin d4, Pin d5, Pin d6, Pin d7)
 55{
 56  _rs_pin = rs;
 57  _rw_pin = rw;
 58  _enable_pin = enable;
 59  
 60  _data_pins[0] = d0;
 61  _data_pins[1] = d1;
 62  _data_pins[2] = d2;
 63  _data_pins[3] = d3; 
 64  _data_pins[4] = d4;
 65  _data_pins[5] = d5;
 66  _data_pins[6] = d6;
 67  _data_pins[7] = d7; 
 68
 69  _rs_pin.setDirection(true);
 70  // we can save 1 pin by not using RW. Indicate by passing 255 instead of pin#
 71  if (!_rw_pin.isNull()) { _rw_pin.setDirection(true); }
 72 _enable_pin.setDirection(true);
 73  
 74  if (fourbitmode)
 75    _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
 76  else 
 77    _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS;
 78  
 79  begin(16, 1);  
 80}
 81
 82void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
 83  if (lines > 1) {
 84    _displayfunction |= LCD_2LINE;
 85  }
 86  _numlines = lines;
 87  _currline = 0;
 88
 89  // for some 1 line displays you can select a 10 pixel high font
 90  if ((dotsize != 0) && (lines == 1)) {
 91    _displayfunction |= LCD_5x10DOTS;
 92  }
 93
 94  // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
 95  // according to datasheet, we need at least 40ms after power rises above 2.7V
 96  // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
 97  _delay_us(50000);
 98  // Now we pull both RS and R/W low to begin commands
 99  _rs_pin.setValue(false);
100  _enable_pin.setValue(false);
101  if (!_rw_pin.isNull()) {
102    _rw_pin.setValue(false);
103  }
104  
105  //put the LCD into 4 bit or 8 bit mode
106  if (! (_displayfunction & LCD_8BITMODE)) {
107    // this is according to the hitachi HD44780 datasheet
108    // figure 24, pg 46
109
110    // we start in 8bit mode, try to set 4 bit mode
111    write4bits(0x03);
112    _delay_us(4500); // wait min 4.1ms
113
114    // second try
115    write4bits(0x03);
116    _delay_us(4500); // wait min 4.1ms
117    
118    // third go!
119    write4bits(0x03); 
120    _delay_us(150);
121
122    // finally, set to 8-bit interface
123    write4bits(0x02); 
124  } else {
125    // this is according to the hitachi HD44780 datasheet
126    // page 45 figure 23
127
128    // Send function set command sequence
129    command(LCD_FUNCTIONSET | _displayfunction);
130    _delay_us(4500);  // wait more than 4.1ms
131
132    // second try
133    command(LCD_FUNCTIONSET | _displayfunction);
134    _delay_us(150);
135
136    // third go
137    command(LCD_FUNCTIONSET | _displayfunction);
138  }
139
140  // finally, set # lines, font size, etc.
141  command(LCD_FUNCTIONSET | _displayfunction);  
142
143  // turn the display on with no cursor or blinking default
144  _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;  
145  display();
146
147  // clear it off
148  clear();
149
150  // Initialize to default text direction (for romance languages)
151  _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
152  // set the entry mode
153  command(LCD_ENTRYMODESET | _displaymode);
154
155}
156
157/********** high level commands, for the user! */
158void LiquidCrystal::clear()
159{
160  command(LCD_CLEARDISPLAY);  // clear display, set cursor position to zero
161  _delay_us(2000);  // this command takes a long time!
162}
163
164void LiquidCrystal::home()
165{
166  command(LCD_RETURNHOME);  // set cursor position to zero
167  _delay_us(2000);  // this command takes a long time!
168}
169
170void LiquidCrystal::setCursor(uint8_t col, uint8_t row)
171{
172  int row_offsets[] = { 0x00, 0x40, 0x10, 0x50 };
173  if ( row > _numlines ) {
174    row = _numlines-1;    // we count rows starting w/0
175  }
176  
177  command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
178}
179
180// Turn the display on/off (quickly)
181void LiquidCrystal::noDisplay() {
182  _displaycontrol &= ~LCD_DISPLAYON;
183  command(LCD_DISPLAYCONTROL | _displaycontrol);
184}
185void LiquidCrystal::display() {
186  _displaycontrol |= LCD_DISPLAYON;
187  command(LCD_DISPLAYCONTROL | _displaycontrol);
188}
189
190// Turns the underline cursor on/off
191void LiquidCrystal::noCursor() {
192  _displaycontrol &= ~LCD_CURSORON;
193  command(LCD_DISPLAYCONTROL | _displaycontrol);
194}
195void LiquidCrystal::cursor() {
196  _displaycontrol |= LCD_CURSORON;
197  command(LCD_DISPLAYCONTROL | _displaycontrol);
198}
199
200// Turn on and off the blinking cursor
201void LiquidCrystal::noBlink() {
202  _displaycontrol &= ~LCD_BLINKON;
203  command(LCD_DISPLAYCONTROL | _displaycontrol);
204}
205void LiquidCrystal::blink() {
206  _displaycontrol |= LCD_BLINKON;
207  command(LCD_DISPLAYCONTROL | _displaycontrol);
208}
209
210// These commands scroll the display without changing the RAM
211void LiquidCrystal::scrollDisplayLeft(void) {
212  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
213}
214void LiquidCrystal::scrollDisplayRight(void) {
215  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
216}
217
218// This is for text that flows Left to Right
219void LiquidCrystal::leftToRight(void) {
220  _displaymode |= LCD_ENTRYLEFT;
221  command(LCD_ENTRYMODESET | _displaymode);
222}
223
224// This is for text that flows Right to Left
225void LiquidCrystal::rightToLeft(void) {
226  _displaymode &= ~LCD_ENTRYLEFT;
227  command(LCD_ENTRYMODESET | _displaymode);
228}
229
230// This will 'right justify' text from the cursor
231void LiquidCrystal::autoscroll(void) {
232  _displaymode |= LCD_ENTRYSHIFTINCREMENT;
233  command(LCD_ENTRYMODESET | _displaymode);
234}
235
236// This will 'left justify' text from the cursor
237void LiquidCrystal::noAutoscroll(void) {
238  _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
239  command(LCD_ENTRYMODESET | _displaymode);
240}
241
242// Allows us to fill the first 8 CGRAM locations
243// with custom characters
244void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) {
245  location &= 0x7; // we only have 8 locations 0-7
246  command(LCD_SETCGRAMADDR | (location << 3));
247  for (int i=0; i<8; i++) {
248    write(charmap[i]);
249  }
250}
251
252/*********** mid level commands, for sending data/cmds */
253
254inline void LiquidCrystal::command(uint8_t value) {
255  send(value, false);
256}
257
258inline void LiquidCrystal::write(uint8_t value) {
259  send(value, true);
260}
261
262
263void LiquidCrystal::writeInt(uint16_t value, uint8_t digits) {
264
265	uint16_t currentDigit;
266	uint16_t nextDigit;
267
268	switch (digits) {
269	case 1:		currentDigit = 10;		break;
270	case 2:		currentDigit = 100;		break;
271	case 3:		currentDigit = 1000;	break;
272	case 4:		currentDigit = 10000;	break;
273	default: 	return;
274	}
275
276	for (uint8_t i = 0; i < digits; i++) {
277		nextDigit = currentDigit/10;
278		write((value%currentDigit)/nextDigit+'0');
279		currentDigit = nextDigit;
280	}
281}
282
283
284void LiquidCrystal::writeString(char message[]) {
285	char* letter = message;
286	while (*letter != 0) {
287		write(*letter);
288		letter++;
289	}
290}
291
292void LiquidCrystal::writeFromPgmspace(const prog_uchar message[]) {
293	char letter;
294	while (letter = pgm_read_byte(message++)) {
295		write(letter);
296	}
297}
298
299/************ low level data pushing commands **********/
300
301// write either command or data, with automatic 4/8-bit selection
302void LiquidCrystal::send(uint8_t value, bool mode) {
303  _rs_pin.setValue(mode);
304
305  // if there is a RW pin indicated, set it low to Write
306  if (!_rw_pin.isNull()) {
307    _rw_pin.setValue(false);
308  }
309  
310  if (_displayfunction & LCD_8BITMODE) {
311    write8bits(value); 
312  } else {
313    write4bits(value>>4);
314    write4bits(value);
315  }
316}
317
318void LiquidCrystal::pulseEnable(void) {
319  _enable_pin.setValue(false);
320  _delay_us(1);
321  _enable_pin.setValue(true);
322  _delay_us(1);    // enable pulse must be >450ns
323  _enable_pin.setValue(false);
324  _delay_us(1);   // commands need > 37us to settle [citation needed]
325}
326
327void LiquidCrystal::write4bits(uint8_t value) {
328  for (int i = 0; i < 4; i++) {
329    _data_pins[i].setDirection(true);
330    _data_pins[i].setValue(((value >> i) & 0x01) != 0);
331  }
332
333  pulseEnable();
334}
335
336void LiquidCrystal::write8bits(uint8_t value) {
337  for (int i = 0; i < 8; i++) {
338    _data_pins[i].setDirection(true);
339    _data_pins[i].setValue(((value >> i) & 0x01) != 0);
340  }
341  
342  pulseEnable();
343}