Initial commit

Fork of TextLCD by Wim Huiskamp

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers TextLCD.cpp Source File

TextLCD.cpp

00001 /* mbed TextLCD Library, for a 4-bit LCD based on HD44780
00002  * Copyright (c) 2007-2010, sford, http://mbed.org
00003  *               2013, v01: WH, Added LCD types, fixed LCD address issues, added Cursor and UDCs 
00004  *               2013, v02: WH, Added I2C and SPI bus interfaces  
00005  *               2013, v03: WH, Added support for LCD40x4 which uses 2 controllers 
00006  *               2013, v04: WH, Added support for Display On/Off, improved 4bit bootprocess
00007  *               2013, v05: WH, Added support for 8x2B, added some UDCs   
00008  *               2013, v06: WH, Added support for devices that use internal DC/DC converters 
00009  *               2013, v07: WH, Added support for backlight and include portdefinitions for LCD2004 Module from DFROBOT 
00010  *
00011  * Permission is hereby granted, free of charge, to any person obtaining a copy
00012  * of this software and associated documentation files (the "Software"), to deal
00013  * in the Software without restriction, including without limitation the rights
00014  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00015  * copies of the Software, and to permit persons to whom the Software is
00016  * furnished to do so, subject to the following conditions:
00017  *
00018  * The above copyright notice and this permission notice shall be included in
00019  * all copies or substantial portions of the Software.
00020  *
00021  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00022  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00023  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00024  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00025  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00026  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00027  * THE SOFTWARE.
00028  */
00029 
00030 #include "TextLCD.h"
00031 #include "mbed.h"
00032 
00033 
00034 
00035  /* Create a TextLCD interface using SPI 
00036   *
00037   * @param spi             SPI Bus
00038   * @param cs              chip select pin (active low)
00039   * @param type            Sets the panel size/addressing mode (default = LCD16x2)
00040   * @param ctrl            LCD controller (default = HD44780)      
00041   */
00042 TextLCD::TextLCD(SPI *spi, PinName cs, PinName rs, LCDType type, LCDCtrl ctrl) :
00043         _e(NC), _bl(NC), _e2(NC),
00044         _d(NC),
00045         _spi(spi),        
00046         _cs(cs),
00047         _rs(rs),
00048         _type(type),
00049         _ctrl(ctrl) {                
00050         
00051   _busType = _SPIBus;
00052 
00053   // Setup the spi for 8 bit data, low steady state clock,
00054   // rising edge capture, with a 500KHz or 1MHz clock rate  
00055   _spi->format(8,0);
00056   _spi->frequency(500000);    
00057   //_spi.frequency(1000000);    
00058 
00059 
00060   // Init the portexpander bus
00061   _lcd_bus = D_LCD_BUS_DEF;
00062   
00063   // write the new data to the portexpander
00064   _setCS(false);  
00065   _spi->write(_lcd_bus);   
00066   _setCS(true);  
00067   
00068   _init();
00069     
00070 }
00071 
00072 
00073 /*  Init the LCD Controller(s)
00074  *  Clear display 
00075  */
00076 void TextLCD::_init() {
00077   
00078   // Select and configure second LCD controller when needed
00079   if(_type==LCD40x4) {
00080     _ctrl_idx=TextLCD::_LCDCtrl_1; // Select 2nd controller
00081     
00082     _initCtrl();                   // Init 2nd controller
00083     
00084     // Secondary LCD controller Clearscreen
00085     _writeCommand(0x01);       // cls, and set cursor to 0    
00086     wait_ms(10);     // The CLS command takes 1.64 ms.
00087                      // Since we are not using the Busy flag, Lets be safe and take 10 ms
00088     
00089   }
00090     
00091   // Select and configure primary LCD controller
00092   _ctrl_idx=TextLCD::_LCDCtrl_0; // Select primary controller  
00093 
00094   _initCtrl();                   // Init primary controller
00095   
00096   // Primary LCD controller Clearscreen
00097   _writeCommand(0x01);       // cls, and set cursor to 0
00098 
00099   wait_ms(10);     // The CLS command takes 1.64 ms.
00100                    // Since we are not using the Busy flag, Lets be safe and take 10 ms
00101     
00102 } 
00103 
00104 /*  Init the LCD controller
00105  *  4-bit mode, number of lines, fonttype, no cursor etc
00106  *  
00107  */
00108 void TextLCD::_initCtrl() {
00109 
00110     _setRS(false);      // command mode
00111     
00112     wait_ms(20);        // Wait 20ms to ensure powered up
00113 
00114 /*    // send "Display Settings" 3 times (Only top nibble of 0x30 as we've got 4-bit bus)    
00115     for (int i=0; i<3; i++) {
00116         _writeNibble(0x3);
00117         wait_ms(15);     // This command takes 1.64ms, so wait for it 
00118     }
00119     _writeNibble(0x2);   // 4-bit mode
00120     wait_us(40);         // most instructions take 40us
00121 */
00122     // Display is now in 4-bit mode
00123 
00124     
00125           // ST7036 controller: Initialise Voltage booster for VLCD. VDD=3.3V
00126           // Note: supports 1,2 or 3 lines
00127           _writeByte( 0x39 );    // 8-bit Databus, 2 Lines, Select Instruction table 1
00128           wait_ms(30);           // > 26,3ms 
00129           _writeByte( 0x14 );    // Bias: 1/5, 2-Lines LCD 
00130           wait_ms(30);           // > 26,3ms
00131           _writeByte( 0x55 );    // Icon off, Booster on, Set Contrast C5, C4
00132           wait_ms(30);           // > 26,3ms
00133           _writeByte( 0x6d );    // Voltagefollower On, Ampl ratio Rab2, Rab1, Rab0
00134           wait_ms(200);          // > 200ms!
00135           _writeByte( 0x78 );    // Set Contrast C3, C2, C1, C0
00136           wait_ms(30);           // > 26,3ms
00137           _writeByte( 0x38 );    // Return to Instruction table 0
00138           wait_ms(50);
00139           _writeByte( 0x0f );    // Return to Instruction table 0
00140           wait_ms(50);
00141           _writeByte( 0x01 );    // Return to Instruction table 0
00142           wait_ms(50);
00143           _writeByte( 0x06 );    // Return to Instruction table 0
00144           wait_ms(50);
00145           
00146 
00147     
00148     // Initialise Display configuration
00149     switch (_type) {
00150         case LCD8x1:
00151         case LCD8x2B:        
00152             //8x1 is a regular 1 line display
00153             //8x2B is a special case of 16x1
00154             _writeCommand(0x20); // Function set 001 DL N F - -
00155                                  //  DL=0 (4 bits bus)             
00156                                  //   N=0 (1 line)
00157                                  //   F=0 (5x7 dots font)
00158             break;                                
00159             
00160         case LCD24x4:
00161             // Special mode for KS0078
00162             _writeCommand(0x2A); // Function set 001 DL N RE DH REV
00163                                  //   DL=0  (4 bits bus)             
00164                                  //    N=1  (Dont care for KS0078)
00165                                  //   RE=0  (Extended Regs, special mode for KS0078)
00166                                  //   DH=1  (Disp shift, special mode for KS0078)                                
00167                                  //   REV=0 (Reverse, special mode for KS0078)
00168 
00169             _writeCommand(0x2E); // Function set 001 DL N RE DH REV
00170                                  //   DL=0  (4 bits bus)             
00171                                  //    N=1  (Dont care for KS0078)
00172                                  //   RE=1  (Ena Extended Regs, special mode for KS0078)
00173                                  //   DH=1  (Disp shift, special mode for KS0078)                                
00174                                  //   REV=0 (Reverse, special mode for KS0078)
00175 
00176             _writeCommand(0x09); // Ext Function set 0000 1 FW BW NW
00177                                  //   FW=0  (5-dot font, special mode for KS0078)
00178                                  //   BW=0  (Cur BW invert disable, special mode for KS0078)
00179                                  //   NW=1  (4 Line, special mode for KS0078)                                
00180 
00181             _writeCommand(0x2A); // Function set 001 DL N RE DH REV
00182                                  //   DL=0  (4 bits bus)             
00183                                  //    N=1  (Dont care for KS0078)
00184                                  //   RE=0  (Dis. Extended Regs, special mode for KS0078)
00185                                  //   DH=1  (Disp shift, special mode for KS0078)                                
00186                                  //   REV=0 (Reverse, special mode for KS0078)
00187             break;
00188                                             
00189 // All other LCD types are initialised as 2 Line displays (including LCD40x4)
00190         default:
00191             _writeCommand(0x39); // Function set 001 DL N F - -
00192                                  //  DL=0 (4 bits bus) 
00193                                  //   N=1 (2 lines)
00194                                  //   F=0 (5x7 dots font, only option for 2 line display)
00195                                  //    -  (Don't care)                                
00196             
00197             break;
00198     }
00199 
00200     _writeCommand(0x06); // Entry Mode 0000 01 CD S 
00201                          //   Cursor Direction and Display Shift
00202                          //   CD=1 (Cur incr)
00203                          //   S=0  (No display shift)                        
00204 
00205 //    _writeCommand(0x0C); // Display Ctrl 0000 1 D C B
00206 //                         //   Display On, Cursor Off, Blink Off   
00207     setCursor(TextLCD::CurOff_BlkOff);     
00208     setMode(TextLCD::DispOn);     
00209 }
00210 
00211 
00212 // Clear the screen, Cursor home. 
00213 void TextLCD::cls() {
00214 
00215   // Select and configure second LCD controller when needed
00216   if(_type==LCD40x4) {
00217     _ctrl_idx=TextLCD::_LCDCtrl_1; // Select 2nd controller
00218 
00219     // Second LCD controller Cursor always Off
00220     _setCursorAndDisplayMode(_currentMode, TextLCD::CurOff_BlkOff);
00221 
00222     // Second LCD controller Clearscreen
00223     _writeCommand(0x01); // cls, and set cursor to 0    
00224 
00225     wait_ms(10);     // The CLS command takes 1.64 ms.
00226                      // Since we are not using the Busy flag, Lets be safe and take 10 ms
00227 
00228   
00229     _ctrl_idx=TextLCD::_LCDCtrl_0; // Select primary controller
00230   }
00231   
00232   // Primary LCD controller Clearscreen
00233   _writeCommand(0x01); // cls, and set cursor to 0
00234 
00235   wait_ms(10);     // The CLS command takes 1.64 ms.
00236                    // Since we are not using the Busy flag, Lets be safe and take 10 ms
00237 
00238   // Restore cursormode on primary LCD controller when needed
00239   if(_type==LCD40x4) {
00240     _setCursorAndDisplayMode(_currentMode,_currentCursor);     
00241   }
00242                    
00243   _row=0;          // Reset Cursor location
00244   _column=0;
00245 }
00246 
00247 // Move cursor to selected row and column
00248 void TextLCD::locate(int column, int row) {
00249     
00250    // setAddress() does all the heavy lifting:
00251    //   check column and row sanity, 
00252    //   switch controllers for LCD40x4 if needed
00253    //   switch cursor for LCD40x4 if needed
00254    //   set the new memory address to show cursor at correct location
00255    setAddress(column, row);
00256        
00257 }
00258     
00259 
00260 // Write a single character (Stream implementation)
00261 int TextLCD::_putc(int value) {
00262   int addr;
00263     
00264     if (value == '\n') {
00265       //No character to write
00266       
00267       //Update Cursor      
00268       _column = 0;
00269       _row++;
00270       if (_row >= rows()) {
00271         _row = 0;
00272       }      
00273     }
00274     else {
00275       //Character to write      
00276       _writeData(value); 
00277               
00278       //Update Cursor
00279       _column++;
00280       if (_column >= columns()) {
00281         _column = 0;
00282         _row++;
00283         if (_row >= rows()) {
00284           _row = 0;
00285         }
00286       }          
00287     } //else
00288 
00289     //Set next memoryaddress, make sure cursor blinks at next location
00290     addr = getAddress(_column, _row);
00291     _writeCommand(0x80 | addr);
00292             
00293     return value;
00294 }
00295 
00296 
00297 // get a single character (Stream implementation)
00298 int TextLCD::_getc() {
00299     return -1;
00300 }
00301 
00302 
00303 // Set RS pin
00304 // Used for mbed pins, I2C bus expander or SPI shifregister
00305 void TextLCD::_setRS(bool value) 
00306 {
00307    if (value)
00308       _rs  = 1;    // Set RS bit 
00309    else  
00310       _rs  = 0;    // Reset RS bit 
00311 }    
00312 
00313 // Place the 4bit data on the databus
00314 // Used for mbed pins, I2C bus expander or SPI shifregister
00315 void TextLCD::_setData(int value) 
00316 {
00317     // write the new data to the SPI portexpander
00318     _setCS(false);  
00319     _spi->write(value);   
00320     _setCS(true);  
00321 }    
00322 
00323 
00324 // Set CS line.
00325 // Only used for SPI bus
00326 void TextLCD::_setCS(bool value) 
00327 {
00328   if (value) {   
00329     _cs  = 1;    // Set CS pin 
00330   }  
00331   else  
00332     _cs  = 0;    // Reset CS pin 
00333   
00334 }
00335 
00336 
00337 
00338 // Write a byte using the 4-bit interface
00339 // Used for mbed pins, I2C bus expander or SPI shifregister
00340 void TextLCD::_writeByte(int value) {
00341 
00342     _setData(value);   // High nibble
00343     wait_us(1); // Data setup time    
00344 }
00345 
00346 void TextLCD::_writeCommand(int command) {
00347 
00348     _setRS(false);        
00349     wait_us(1);  // Data setup time for RS       
00350     
00351     _writeByte(command);   
00352     wait_us(40); // most instructions take 40us            
00353 }
00354 
00355 void TextLCD::_writeData(int data) {
00356 
00357     _setRS(true);            
00358     wait_us(1);  // Data setup time for RS 
00359         
00360     _writeByte(data);
00361     wait_us(40); // data writes take 40us                
00362 }
00363 
00364 
00365 
00366 
00367 // This replaces the original _address() method.
00368 // Left it in here for compatibility with older code. New applications should use getAddress() instead.
00369 int TextLCD::_address(int column, int row) {
00370   return 0x80 | getAddress(column, row);
00371 }
00372 
00373 // This is new method to return the memory address based on row, column and displaytype.
00374 //
00375 int TextLCD::getAddress(int column, int row) {
00376 
00377     switch (_type) {
00378         case LCD8x1:
00379             return 0x00 + column;                        
00380 
00381         case LCD8x2B:
00382             // LCD8x2B is a special layout of LCD16x1
00383             if (row==0) 
00384               return 0x00 + column;                        
00385             else   
00386               return 0x08 + column;                        
00387 
00388 
00389         case LCD16x1:
00390             // LCD16x1 is a special layout of LCD8x2
00391             if (column<8) 
00392               return 0x00 + column;                        
00393             else   
00394               return 0x40 + (column - 8);                        
00395 
00396         case LCD12x4:
00397             switch (row) {
00398                 case 0:
00399                     return 0x00 + column;
00400                 case 1:
00401                     return 0x40 + column;
00402                 case 2:
00403                     return 0x0C + column;
00404                 case 3:
00405                     return 0x4C + column;
00406             }
00407 
00408         case LCD16x4:
00409             switch (row) {
00410                 case 0:
00411                     return 0x00 + column;
00412                 case 1:
00413                     return 0x40 + column;
00414                 case 2:
00415                     return 0x10 + column;
00416                 case 3:
00417                     return 0x50 + column;
00418             }
00419 
00420         case LCD20x4:
00421             switch (row) {
00422                 case 0:
00423                     return 0x00 + column;
00424                 case 1:
00425                     return 0x40 + column;
00426                 case 2:
00427                     return 0x14 + column;
00428                 case 3:
00429                     return 0x54 + column;
00430             }
00431 
00432 // Special mode for KS0078
00433         case LCD24x4:
00434             switch (row) {
00435                 case 0:
00436                     return 0x00 + column;
00437                 case 1:
00438                     return 0x20 + column;
00439                 case 2:
00440                     return 0x40 + column;
00441                 case 3:
00442                     return 0x60 + column;
00443             }
00444 
00445 // Not sure about this one, seems wrong.
00446         case LCD16x2B:      
00447             return 0x00 + (row * 40) + column;
00448       
00449         case LCD8x2:               
00450         case LCD12x2:                
00451         case LCD16x2:
00452         case LCD20x2:
00453         case LCD24x2:        
00454         case LCD40x2:                
00455             return 0x00 + (row * 0x40) + column;
00456 
00457         case LCD40x4:                
00458           // LCD40x4 is a special case since it has 2 controllers
00459           // Each controller is configured as 40x2
00460           if (row<2) { 
00461             // Test to see if we need to switch between controllers  
00462             if (_ctrl_idx != _LCDCtrl_0) {
00463 
00464               // Second LCD controller Cursor Off
00465               _setCursorAndDisplayMode(_currentMode, TextLCD::CurOff_BlkOff);    
00466 
00467               // Select primary controller
00468               _ctrl_idx = _LCDCtrl_0;
00469 
00470               // Restore cursormode on primary LCD controller
00471               _setCursorAndDisplayMode(_currentMode, _currentCursor);    
00472             }           
00473             
00474             return 0x00 + (row * 0x40) + column;          
00475           }
00476           else {
00477 
00478             // Test to see if we need to switch between controllers  
00479             if (_ctrl_idx != _LCDCtrl_1) {
00480               // Primary LCD controller Cursor Off
00481               _setCursorAndDisplayMode(_currentMode, TextLCD::CurOff_BlkOff);    
00482 
00483               // Select secondary controller
00484               _ctrl_idx = _LCDCtrl_1;
00485 
00486               // Restore cursormode on secondary LCD controller
00487               _setCursorAndDisplayMode(_currentMode, _currentCursor);    
00488             }           
00489                                    
00490             return 0x00 + ((row-2) * 0x40) + column;          
00491           } 
00492             
00493 // Should never get here.
00494         default:            
00495             return 0x00;        
00496     }
00497 }
00498 
00499 
00500 // Set row, column and update memoryaddress.
00501 //
00502 void TextLCD::setAddress(int column, int row) {
00503    
00504 // Sanity Check column
00505     if (column < 0) {
00506       _column = 0;
00507     }
00508     else if (column >= columns()) {
00509       _column = columns() - 1;
00510     } else _column = column;
00511     
00512 // Sanity Check row
00513     if (row < 0) {
00514       _row = 0;
00515     }
00516     else if (row >= rows()) {
00517       _row = rows() - 1;
00518     } else _row = row;
00519     
00520     
00521 // Compute the memory address
00522 // For LCD40x4:  switch controllers if needed
00523 //               switch cursor if needed
00524     int addr = getAddress(_column, _row);
00525     
00526     _writeCommand(0x80 | addr);
00527 }
00528 
00529 int TextLCD::columns() {
00530     switch (_type) {
00531         case LCD8x1:
00532         case LCD8x2:
00533         case LCD8x2B:                
00534             return 8;
00535         
00536         case LCD12x2:        
00537         case LCD12x4:        
00538             return 12;        
00539 
00540         case LCD16x1:        
00541         case LCD16x2:
00542         case LCD16x2B:
00543         case LCD16x4:        
00544             return 16;
00545             
00546         case LCD20x2:
00547         case LCD20x4:
00548             return 20;
00549 
00550         case LCD24x2:
00551         case LCD24x4:        
00552             return 24;        
00553 
00554         case LCD40x2:
00555         case LCD40x4:
00556             return 40;        
00557         
00558 // Should never get here.
00559         default:
00560             return 0;
00561     }
00562 }
00563 
00564 int TextLCD::rows() {
00565     switch (_type) {
00566         case LCD8x1: 
00567         case LCD16x1:         
00568             return 1;           
00569 
00570         case LCD8x2:  
00571         case LCD8x2B:                        
00572         case LCD12x2:                      
00573         case LCD16x2:
00574         case LCD16x2B:
00575         case LCD20x2:
00576         case LCD24x2:        
00577         case LCD40x2:                
00578             return 2;
00579                     
00580         case LCD12x4:        
00581         case LCD16x4:
00582         case LCD20x4:
00583         case LCD24x4:        
00584         case LCD40x4:
00585             return 4;
00586 
00587 // Should never get here.      
00588         default:
00589             return 0;        
00590     }
00591 }
00592 
00593 
00594 // Set the Cursor Mode (Cursor Off & Blink Off, Cursor On & Blink Off, Cursor Off & Blink On, Cursor On & Blink On
00595 void TextLCD::setCursor(TextLCD::LCDCursor cursorMode) { 
00596 
00597   // Save new cursor mode, needed when 2 controllers are in use or when display is switched off/on
00598   _currentCursor = cursorMode;
00599     
00600   // Configure only current LCD controller
00601   _setCursorAndDisplayMode(_currentMode, _currentCursor);
00602     
00603 }
00604 
00605 // Set the Displaymode (On/Off)
00606 void TextLCD::setMode(TextLCD::LCDMode displayMode) { 
00607 
00608   // Save new displayMode, needed when 2 controllers are in use or when cursor is changed
00609   _currentMode = displayMode;
00610     
00611   // Select and configure second LCD controller when needed
00612   if(_type==LCD40x4) {
00613     if (_ctrl_idx==TextLCD::_LCDCtrl_0) {
00614       // Configure primary LCD controller
00615       _setCursorAndDisplayMode(_currentMode, _currentCursor);
00616 
00617       // Select 2nd controller
00618       _ctrl_idx=TextLCD::_LCDCtrl_1;
00619   
00620       // Configure secondary LCD controller    
00621       _setCursorAndDisplayMode(_currentMode, TextLCD::CurOff_BlkOff);
00622 
00623       // Restore current controller
00624       _ctrl_idx=TextLCD::_LCDCtrl_0;       
00625     }
00626     else {
00627       // Select primary controller
00628       _ctrl_idx=TextLCD::_LCDCtrl_0;
00629     
00630       // Configure primary LCD controller
00631       _setCursorAndDisplayMode(_currentMode, TextLCD::CurOff_BlkOff);
00632        
00633       // Restore current controller
00634       _ctrl_idx=TextLCD::_LCDCtrl_1;
00635 
00636       // Configure secondary LCD controller    
00637       _setCursorAndDisplayMode(_currentMode, _currentCursor);
00638 
00639     }
00640   }
00641   else {
00642     // Configure primary LCD controller
00643     _setCursorAndDisplayMode(_currentMode, _currentCursor);
00644   }   
00645     
00646 }
00647 
00648 
00649 // Set the Displaymode (On/Off) and Cursortype for current controller
00650 void TextLCD::_setCursorAndDisplayMode(TextLCD::LCDMode displayMode, TextLCD::LCDCursor cursorType) { 
00651     
00652     // Configure current LCD controller       
00653     _writeCommand(0x08 | displayMode | cursorType);
00654 }
00655 
00656 
00657 
00658 void TextLCD::setUDC(unsigned char c, char *udc_data) {
00659   
00660   // Select and configure second LCD controller when needed
00661   if(_type==LCD40x4) {
00662     _LCDCtrl_Idx current_ctrl_idx = _ctrl_idx; // Temp save current controller
00663    
00664     // Select primary controller     
00665     _ctrl_idx=TextLCD::_LCDCtrl_0;
00666     
00667     // Configure primary LCD controller
00668     _setUDC(c, udc_data);
00669 
00670     // Select 2nd controller
00671     _ctrl_idx=TextLCD::_LCDCtrl_1;
00672   
00673     // Configure secondary LCD controller    
00674     _setUDC(c, udc_data);
00675 
00676     // Restore current controller
00677     _ctrl_idx=current_ctrl_idx;       
00678   }
00679   else {
00680     // Configure primary LCD controller
00681     _setUDC(c, udc_data); 
00682   }
00683     
00684 }
00685 
00686 void TextLCD::_setUDC(unsigned char c, char *udc_data) {
00687   
00688   // Select CG RAM for current LCD controller
00689   _writeCommand(0x40 + ((c & 0x07) << 3)); //Set CG-RAM address,
00690                                            //8 sequential locations needed per UDC
00691   // Store UDC pattern 
00692   for (int i=0; i<8; i++) {
00693     _writeData(*udc_data++);
00694   }
00695    
00696   //Select DD RAM again for current LCD controller
00697   int addr = getAddress(_column, _row);
00698   _writeCommand(0x80 | addr);
00699   
00700 }