Subversion Repositories HomeAutomation

Rev

Rev 604 | Rev 646 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | SVN | RSS feed

  1. /****************************************************************************
  2.  Title  :   HD44780U LCD library
  3.  Author:    Peter Fleury <pfleury@gmx.ch>  http://jump.to/fleury
  4.  File:      $Id: lcd.c,v 1.14.2.1 2006/01/29 12:16:41 peter Exp $
  5.  Software:  AVR-GCC 3.3
  6.  Target:    any AVR device, memory mapped mode only for AT90S4414/8515/Mega
  7.  
  8.  DESCRIPTION
  9.        Basic routines for interfacing a HD44780U-based text lcd display
  10.  
  11.        Originally based on Volker Oth's lcd library,
  12.        changed lcd_init(), added additional constants for lcd_command(),
  13.        added 4-bit I/O mode, improved and optimized code.
  14.  
  15.        Library can be operated in memory mapped mode (LCD_IO_MODE=0) or in
  16.        4-bit IO port mode (LCD_IO_MODE=1). 8-bit IO port mode not supported.
  17.        
  18.        Memory mapped mode compatible with Kanda STK200, but supports also
  19.        generation of R/W signal through A8 address line.
  20.  
  21.  USAGE
  22.        See the C include lcd.h file for a description of each function
  23.        
  24. *****************************************************************************/
  25. #include <inttypes.h>
  26. #include <avr/io.h>
  27. #include <avr/pgmspace.h>
  28. #include "lcd_HD44780.h"
  29. #include <config.h>
  30.  
  31.  
  32. // custom LCD characters
  33. unsigned char __attribute__ ((progmem)) LcdCustomChar[] =
  34. {
  35.     0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, // 0. 0/5 full progress block
  36.     0x00, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x00, // 1. 1/5 full progress block
  37.     0x00, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, // 2. 2/5 full progress block
  38.     0x00, 0x1F, 0x1C, 0x1C, 0x1C, 0x1C, 0x1F, 0x00, // 3. 3/5 full progress block
  39.     0x00, 0x1F, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x00, // 4. 4/5 full progress block
  40.     0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, // 5. 5/5 full progress block
  41. };
  42.  
  43.  
  44. /*
  45. ** constants/macros
  46. */
  47. #define DDR(x) (*(&x - 1))      /* address of data direction register of port x */
  48. #if defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__)
  49.     /* on ATmega64/128 PINF is on port 0x00 and not 0x60 */
  50.     #define PIN(x) ( &PORTF==&(x) ? _SFR_IO8(0x00) : (*(&x - 2)) )
  51. #else
  52.     #define PIN(x) (*(&x - 2))    /* address of input register of port x          */
  53. #endif
  54.  
  55.  
  56. #if LCD_IO_MODE
  57. #define lcd_e_delay()   __asm__ __volatile__( "rjmp 1f\n 1:" );
  58. #define lcd_e_high()    LCD_E_PORT  |=  _BV(LCD_E_PIN);
  59. #define lcd_e_low()     LCD_E_PORT  &= ~_BV(LCD_E_PIN);
  60. #define lcd_e_toggle()  toggle_e()
  61. #define lcd_rw_high()   LCD_RW_PORT |=  _BV(LCD_RW_PIN)
  62. #define lcd_rw_low()    LCD_RW_PORT &= ~_BV(LCD_RW_PIN)
  63. #define lcd_rs_high()   LCD_RS_PORT |=  _BV(LCD_RS_PIN)
  64. #define lcd_rs_low()    LCD_RS_PORT &= ~_BV(LCD_RS_PIN)
  65. #endif
  66.  
  67. #if LCD_IO_MODE
  68. #if LCD_LINES==1
  69. #define LCD_FUNCTION_DEFAULT    LCD_FUNCTION_4BIT_1LINE
  70. #else
  71. #define LCD_FUNCTION_DEFAULT    LCD_FUNCTION_4BIT_2LINES
  72. #endif
  73. #else
  74. #if LCD_LINES==1
  75. #define LCD_FUNCTION_DEFAULT    LCD_FUNCTION_8BIT_1LINE
  76. #else
  77. #define LCD_FUNCTION_DEFAULT    LCD_FUNCTION_8BIT_2LINES
  78. #endif
  79. #endif
  80.  
  81. #if LCD_CONTROLLER_KS0073
  82. #if LCD_LINES==4
  83.  
  84. #define KS0073_EXTENDED_FUNCTION_REGISTER_ON  0x24   /* |0|010|0100 4-bit mode extension-bit RE = 1 */
  85. #define KS0073_EXTENDED_FUNCTION_REGISTER_OFF 0x20   /* |0|000|1001 4 lines mode */
  86. #define KS0073_4LINES_MODE                    0x09   /* |0|001|0000 4-bit mode, extension-bit RE = 0 */
  87.  
  88. #endif
  89. #endif
  90.  
  91. /*
  92. ** function prototypes
  93. */
  94. #if LCD_IO_MODE
  95. static void toggle_e(void);
  96. #endif
  97.  
  98. /*
  99. ** local functions
  100. */
  101.  
  102.  
  103.  
  104. /*************************************************************************
  105.  delay loop for small accurate delays: 16-bit counter, 4 cycles/loop
  106. *************************************************************************/
  107. static inline void _delayFourCycles(unsigned int __count)
  108. {
  109.     if ( __count == 0 )    
  110.         __asm__ __volatile__( "rjmp 1f\n 1:" );    // 2 cycles
  111.     else
  112.         __asm__ __volatile__ (
  113.             "1: sbiw %0,1" "\n\t"                  
  114.             "brne 1b"                              // 4 cycles/loop
  115.             : "=w" (__count)
  116.             : "0" (__count)
  117.            );
  118. }
  119.  
  120.  
  121. /*************************************************************************
  122. delay for a minimum of <us> microseconds
  123. the number of loops is calculated at compile-time from MCU clock frequency
  124. *************************************************************************/
  125. #define delay(us)  _delayFourCycles( ( ( 1*(F_OSC/4000) )*us)/1000 )
  126.  
  127.  
  128. #if LCD_IO_MODE
  129. /* toggle Enable Pin to initiate write */
  130. static void toggle_e(void)
  131. {
  132.     lcd_e_high();
  133.     lcd_e_delay();
  134.     lcd_e_low();
  135. }
  136. #endif
  137.  
  138.  
  139. /*************************************************************************
  140. Low-level function to write byte to LCD controller
  141. Input:    data   byte to write to LCD
  142.           rs     1: write data    
  143.                  0: write instruction
  144. Returns:  none
  145. *************************************************************************/
  146. #if LCD_IO_MODE
  147. static void lcd_write(uint8_t data,uint8_t rs)
  148. {
  149.     unsigned char dataBits ;
  150.  
  151.  
  152.     if (rs) {   /* write data        (RS=1, RW=0) */
  153.        lcd_rs_high();
  154.     } else {    /* write instruction (RS=0, RW=0) */
  155.        lcd_rs_low();
  156.     }
  157.     lcd_rw_low();
  158.  
  159.     if ( ( &LCD_DATA0_PORT == &LCD_DATA1_PORT) && ( &LCD_DATA1_PORT == &LCD_DATA2_PORT ) && ( &LCD_DATA2_PORT == &LCD_DATA3_PORT )
  160.       && (LCD_DATA0_PIN == 0) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3) )
  161.     {
  162.         /* configure data pins as output */
  163.         DDR(LCD_DATA0_PORT) |= 0x0F;
  164.  
  165.         /* output high nibble first */
  166.         dataBits = LCD_DATA0_PORT & 0xF0;
  167.         LCD_DATA0_PORT = dataBits |((data>>4)&0x0F);
  168.         lcd_e_toggle();
  169.  
  170.         /* output low nibble */
  171.         LCD_DATA0_PORT = dataBits | (data&0x0F);
  172.         lcd_e_toggle();
  173.  
  174.         /* all data pins high (inactive) */
  175.         LCD_DATA0_PORT = dataBits | 0x0F;
  176.     }
  177.     else
  178.     {
  179.         /* configure data pins as output */
  180.         DDR(LCD_DATA0_PORT) |= _BV(LCD_DATA0_PIN);
  181.         DDR(LCD_DATA1_PORT) |= _BV(LCD_DATA1_PIN);
  182.         DDR(LCD_DATA2_PORT) |= _BV(LCD_DATA2_PIN);
  183.         DDR(LCD_DATA3_PORT) |= _BV(LCD_DATA3_PIN);
  184.        
  185.         /* output high nibble first */
  186.         LCD_DATA3_PORT &= ~_BV(LCD_DATA3_PIN);
  187.         LCD_DATA2_PORT &= ~_BV(LCD_DATA2_PIN);
  188.         LCD_DATA1_PORT &= ~_BV(LCD_DATA1_PIN);
  189.         LCD_DATA0_PORT &= ~_BV(LCD_DATA0_PIN);
  190.         if(data & 0x80) LCD_DATA3_PORT |= _BV(LCD_DATA3_PIN);
  191.         if(data & 0x40) LCD_DATA2_PORT |= _BV(LCD_DATA2_PIN);
  192.         if(data & 0x20) LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN);
  193.         if(data & 0x10) LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN);  
  194.         lcd_e_toggle();
  195.        
  196.         /* output low nibble */
  197.         LCD_DATA3_PORT &= ~_BV(LCD_DATA3_PIN);
  198.         LCD_DATA2_PORT &= ~_BV(LCD_DATA2_PIN);
  199.         LCD_DATA1_PORT &= ~_BV(LCD_DATA1_PIN);
  200.         LCD_DATA0_PORT &= ~_BV(LCD_DATA0_PIN);
  201.         if(data & 0x08) LCD_DATA3_PORT |= _BV(LCD_DATA3_PIN);
  202.         if(data & 0x04) LCD_DATA2_PORT |= _BV(LCD_DATA2_PIN);
  203.         if(data & 0x02) LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN);
  204.         if(data & 0x01) LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN);
  205.         lcd_e_toggle();        
  206.        
  207.         /* all data pins high (inactive) */
  208.         LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN);
  209.         LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN);
  210.         LCD_DATA2_PORT |= _BV(LCD_DATA2_PIN);
  211.         LCD_DATA3_PORT |= _BV(LCD_DATA3_PIN);
  212.     }
  213. }
  214. #else
  215. #define lcd_write(d,rs) if (rs) *(volatile uint8_t*)(LCD_IO_DATA) = d; else *(volatile uint8_t*)(LCD_IO_FUNCTION) = d;
  216. /* rs==0 -> write instruction to LCD_IO_FUNCTION */
  217. /* rs==1 -> write data to LCD_IO_DATA */
  218. #endif
  219.  
  220.  
  221. /*************************************************************************
  222. Low-level function to read byte from LCD controller
  223. Input:    rs     1: read data    
  224.                  0: read busy flag / address counter
  225. Returns:  byte read from LCD controller
  226. *************************************************************************/
  227. #if LCD_IO_MODE
  228. static uint8_t lcd_read(uint8_t rs)
  229. {
  230.     uint8_t data;
  231.    
  232.    
  233.     if (rs)
  234.         lcd_rs_high();                       /* RS=1: read data      */
  235.     else
  236.         lcd_rs_low();                        /* RS=0: read busy flag */
  237.     lcd_rw_high();                           /* RW=1  read mode      */
  238.    
  239.     if ( ( &LCD_DATA0_PORT == &LCD_DATA1_PORT) && ( &LCD_DATA1_PORT == &LCD_DATA2_PORT ) && ( &LCD_DATA2_PORT == &LCD_DATA3_PORT )
  240.       && ( LCD_DATA0_PIN == 0 )&& (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3) )
  241.     {
  242.         DDR(LCD_DATA0_PORT) &= 0xF0;         /* configure data pins as input */
  243.        
  244.         lcd_e_high();
  245.         lcd_e_delay();        
  246.         data = PIN(LCD_DATA0_PORT) << 4;     /* read high nibble first */
  247.         lcd_e_low();
  248.        
  249.         lcd_e_delay();                       /* Enable 500ns low       */
  250.        
  251.         lcd_e_high();
  252.         lcd_e_delay();
  253.         data |= PIN(LCD_DATA0_PORT)&0x0F;    /* read low nibble        */
  254.         lcd_e_low();
  255.     }
  256.     else
  257.     {
  258.         /* configure data pins as input */
  259.         DDR(LCD_DATA0_PORT) &= ~_BV(LCD_DATA0_PIN);
  260.         DDR(LCD_DATA1_PORT) &= ~_BV(LCD_DATA1_PIN);
  261.         DDR(LCD_DATA2_PORT) &= ~_BV(LCD_DATA2_PIN);
  262.         DDR(LCD_DATA3_PORT) &= ~_BV(LCD_DATA3_PIN);
  263.                
  264.         /* read high nibble first */
  265.         lcd_e_high();
  266.         lcd_e_delay();        
  267.         data = 0;
  268.         if ( PIN(LCD_DATA0_PORT) & _BV(LCD_DATA0_PIN) ) data |= 0x10;
  269.         if ( PIN(LCD_DATA1_PORT) & _BV(LCD_DATA1_PIN) ) data |= 0x20;
  270.         if ( PIN(LCD_DATA2_PORT) & _BV(LCD_DATA2_PIN) ) data |= 0x40;
  271.         if ( PIN(LCD_DATA3_PORT) & _BV(LCD_DATA3_PIN) ) data |= 0x80;
  272.         lcd_e_low();
  273.  
  274.         lcd_e_delay();                       /* Enable 500ns low       */
  275.    
  276.         /* read low nibble */    
  277.         lcd_e_high();
  278.         lcd_e_delay();
  279.         if ( PIN(LCD_DATA0_PORT) & _BV(LCD_DATA0_PIN) ) data |= 0x01;
  280.         if ( PIN(LCD_DATA1_PORT) & _BV(LCD_DATA1_PIN) ) data |= 0x02;
  281.         if ( PIN(LCD_DATA2_PORT) & _BV(LCD_DATA2_PIN) ) data |= 0x04;
  282.         if ( PIN(LCD_DATA3_PORT) & _BV(LCD_DATA3_PIN) ) data |= 0x08;        
  283.         lcd_e_low();
  284.     }
  285.     return data;
  286. }
  287. #else
  288. #define lcd_read(rs) (rs) ? *(volatile uint8_t*)(LCD_IO_DATA+LCD_IO_READ) : *(volatile uint8_t*)(LCD_IO_FUNCTION+LCD_IO_READ)
  289. /* rs==0 -> read instruction from LCD_IO_FUNCTION */
  290. /* rs==1 -> read data from LCD_IO_DATA */
  291. #endif
  292.  
  293.  
  294. /*************************************************************************
  295. loops while lcd is busy, returns address counter
  296. *************************************************************************/
  297. static uint8_t lcd_waitbusy(void)
  298.  
  299. {
  300.     register uint8_t c;
  301.    
  302.     /* wait until busy flag is cleared */
  303.     while ( (c=lcd_read(0)) & (1<<LCD_BUSY)) {}
  304.    
  305.     /* the address counter is updated 4us after the busy flag is cleared */
  306.     delay(2);
  307.  
  308.     /* now read the address counter */
  309.     return (lcd_read(0));  // return address counter
  310.    
  311. }/* lcd_waitbusy */
  312.  
  313.  
  314. /*************************************************************************
  315. Move cursor to the start of next line or to the first line if the cursor
  316. is already on the last line.
  317. *************************************************************************/
  318. static inline void lcd_newline(uint8_t pos)
  319. {
  320.     register uint8_t addressCounter;
  321.  
  322.  
  323. #if LCD_LINES==1
  324.     addressCounter = 0;
  325. #endif
  326. #if LCD_LINES==2
  327.     if ( pos < (LCD_START_LINE2) )
  328.         addressCounter = LCD_START_LINE2;
  329.     else
  330.         addressCounter = LCD_START_LINE1;
  331. #endif
  332. #if LCD_LINES==4
  333. #if KS0073_4LINES_MODE
  334.     if ( pos < LCD_START_LINE2 )
  335.         addressCounter = LCD_START_LINE2;
  336.     else if ( (pos >= LCD_START_LINE2) && (pos < LCD_START_LINE3) )
  337.         addressCounter = LCD_START_LINE3;
  338.     else if ( (pos >= LCD_START_LINE3) && (pos < LCD_START_LINE4) )
  339.         addressCounter = LCD_START_LINE4;
  340.     else
  341.         addressCounter = LCD_START_LINE1;
  342. #else
  343.     if ( pos < LCD_START_LINE3 )
  344.         addressCounter = LCD_START_LINE2;
  345.     else if ( (pos >= LCD_START_LINE2) && (pos < LCD_START_LINE4) )
  346.         addressCounter = LCD_START_LINE3;
  347.     else if ( (pos >= LCD_START_LINE3) && (pos < LCD_START_LINE2) )
  348.         addressCounter = LCD_START_LINE4;
  349.     else
  350.         addressCounter = LCD_START_LINE1;
  351. #endif
  352. #endif
  353.     lcd_command((1<<LCD_DDRAM)+addressCounter);
  354.  
  355. }/* lcd_newline */
  356.  
  357.  
  358. /*
  359. ** PUBLIC FUNCTIONS
  360. */
  361.  
  362. /*************************************************************************
  363. Send LCD controller instruction command
  364. Input:   instruction to send to LCD controller, see HD44780 data sheet
  365. Returns: none
  366. *************************************************************************/
  367. void lcd_command(uint8_t cmd)
  368. {
  369.     lcd_waitbusy();
  370.     lcd_write(cmd,0);
  371. }
  372.  
  373.  
  374. /*************************************************************************
  375. Send data byte to LCD controller
  376. Input:   data to send to LCD controller, see HD44780 data sheet
  377. Returns: none
  378. *************************************************************************/
  379. void lcd_data(uint8_t data)
  380. {
  381.     lcd_waitbusy();
  382.     lcd_write(data,1);
  383. }
  384.  
  385.  
  386.  
  387. /*************************************************************************
  388. Set cursor to specified position
  389. Input:    x  horizontal position  (0: left most position)
  390.           y  vertical position    (0: first line)
  391. Returns:  none
  392. *************************************************************************/
  393. void lcd_gotoxy(uint8_t x, uint8_t y)
  394. {
  395. #if LCD_LINES==1
  396.     lcd_command((1<<LCD_DDRAM)+LCD_START_LINE1+x);
  397. #endif
  398. #if LCD_LINES==2
  399.     if ( y==0 )
  400.         lcd_command((1<<LCD_DDRAM)+LCD_START_LINE1+x);
  401.     else
  402.         lcd_command((1<<LCD_DDRAM)+LCD_START_LINE2+x);
  403. #endif
  404. #if LCD_LINES==4
  405.     if ( y==0 )
  406.         lcd_command((1<<LCD_DDRAM)+LCD_START_LINE1+x);
  407.     else if ( y==1)
  408.         lcd_command((1<<LCD_DDRAM)+LCD_START_LINE2+x);
  409.     else if ( y==2)
  410.         lcd_command((1<<LCD_DDRAM)+LCD_START_LINE3+x);
  411.     else /* y==3 */
  412.         lcd_command((1<<LCD_DDRAM)+LCD_START_LINE4+x);
  413. #endif
  414.  
  415. }/* lcd_gotoxy */
  416.  
  417.  
  418. /*************************************************************************
  419. *************************************************************************/
  420. int lcd_getxy(void)
  421. {
  422.     return lcd_waitbusy();
  423. }
  424.  
  425.  
  426. /*************************************************************************
  427. Clear display and set cursor to home position
  428. *************************************************************************/
  429. void lcd_clrscr(void)
  430. {
  431.     lcd_command(1<<LCD_CLR);
  432. }
  433.  
  434.  
  435. /*************************************************************************
  436. Set cursor to home position
  437. *************************************************************************/
  438. void lcd_home(void)
  439. {
  440.     lcd_command(1<<LCD_HOME);
  441. }
  442.  
  443.  
  444. /*************************************************************************
  445. Display character at current cursor position
  446. Input:    character to be displayed                                      
  447. Returns:  none
  448. *************************************************************************/
  449. void lcd_putc(char c)
  450. {
  451.     uint8_t pos;
  452.  
  453.  
  454.     pos = lcd_waitbusy();   // read busy-flag and address counter
  455.     if (c=='\n')
  456.     {
  457.         lcd_newline(pos);
  458.     }
  459.     else
  460.     {
  461. #if LCD_WRAP_LINES==1
  462. #if LCD_LINES==1
  463.         if ( pos == LCD_START_LINE1+LCD_DISP_LENGTH ) {
  464.             lcd_write((1<<LCD_DDRAM)+LCD_START_LINE1,0);
  465.         }
  466. #elif LCD_LINES==2
  467.         if ( pos == LCD_START_LINE1+LCD_DISP_LENGTH ) {
  468.             lcd_write((1<<LCD_DDRAM)+LCD_START_LINE2,0);    
  469.         }else if ( pos == LCD_START_LINE2+LCD_DISP_LENGTH ){
  470.             lcd_write((1<<LCD_DDRAM)+LCD_START_LINE1,0);
  471.         }
  472. #elif LCD_LINES==4
  473.         if ( pos == LCD_START_LINE1+LCD_DISP_LENGTH ) {
  474.             lcd_write((1<<LCD_DDRAM)+LCD_START_LINE2,0);    
  475.         }else if ( pos == LCD_START_LINE2+LCD_DISP_LENGTH ) {
  476.             lcd_write((1<<LCD_DDRAM)+LCD_START_LINE3,0);
  477.         }else if ( pos == LCD_START_LINE3+LCD_DISP_LENGTH ) {
  478.             lcd_write((1<<LCD_DDRAM)+LCD_START_LINE4,0);
  479.         }else if ( pos == LCD_START_LINE4+LCD_DISP_LENGTH ) {
  480.             lcd_write((1<<LCD_DDRAM)+LCD_START_LINE1,0);
  481.         }
  482. #endif
  483.         lcd_waitbusy();
  484. #endif
  485.         lcd_write(c, 1);
  486.     }
  487.  
  488. }/* lcd_putc */
  489.  
  490.  
  491. /*************************************************************************
  492. Display string without auto linefeed
  493. Input:    string to be displayed
  494. Returns:  none
  495. *************************************************************************/
  496. void lcd_puts(const char *s)
  497. /* print string on lcd (no auto linefeed) */
  498. {
  499.     register char c;
  500.  
  501.     while ( (c = *s++) ) {
  502.         lcd_putc(c);
  503.     }
  504.  
  505. }/* lcd_puts */
  506.  
  507.  
  508. /*************************************************************************
  509. Display string from program memory without auto linefeed
  510. Input:     string from program memory be be displayed                                        
  511. Returns:   none
  512. *************************************************************************/
  513. void lcd_puts_p(const char *progmem_s)
  514. /* print string from program memory on lcd (no auto linefeed) */
  515. {
  516.     register char c;
  517.  
  518.     while ( (c = pgm_read_byte(progmem_s++)) ) {
  519.         lcd_putc(c);
  520.     }
  521.  
  522. }/* lcd_puts_p */
  523.  
  524.  
  525. /*************************************************************************
  526. Initialize display and select type of cursor
  527. Input:    dispAttr LCD_DISP_OFF            display off
  528.                    LCD_DISP_ON             display on, cursor off
  529.                    LCD_DISP_ON_CURSOR      display on, cursor on
  530.                    LCD_DISP_CURSOR_BLINK   display on, cursor on flashing
  531. Returns:  none
  532. *************************************************************************/
  533. void lcd_init(uint8_t dispAttr)
  534. {
  535. #if LCD_IO_MODE
  536.     /*
  537.      *  Initialize LCD to 4 bit I/O mode
  538.      */
  539. //    LED_ON
  540.  
  541.     if ( ( &LCD_DATA0_PORT == &LCD_DATA1_PORT) && ( &LCD_DATA1_PORT == &LCD_DATA2_PORT ) && ( &LCD_DATA2_PORT == &LCD_DATA3_PORT )
  542.       && ( &LCD_RS_PORT == &LCD_DATA0_PORT) && ( &LCD_RW_PORT == &LCD_DATA0_PORT) && (&LCD_E_PORT == &LCD_DATA0_PORT)
  543.       && (LCD_DATA0_PIN == 0 ) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3)
  544.       && (LCD_RS_PIN == 4 ) && (LCD_RW_PIN == 5) && (LCD_E_PIN == 6 ) )
  545.     {
  546.         /* configure all port bits as output (all LCD lines on same port) */
  547.         DDR(LCD_DATA0_PORT) |= 0x7F;
  548.     }
  549.     else if ( ( &LCD_DATA0_PORT == &LCD_DATA1_PORT) && ( &LCD_DATA1_PORT == &LCD_DATA2_PORT ) && ( &LCD_DATA2_PORT == &LCD_DATA3_PORT )
  550.            && (LCD_DATA0_PIN == 0 ) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3) )
  551.     {
  552.         /* configure all port bits as output (all LCD data lines on same port, but control lines on different ports) */
  553.         DDR(LCD_DATA0_PORT) |= 0x0F;
  554.         DDR(LCD_RS_PORT)    |= _BV(LCD_RS_PIN);
  555.         DDR(LCD_RW_PORT)    |= _BV(LCD_RW_PIN);
  556.         DDR(LCD_E_PORT)     |= _BV(LCD_E_PIN);
  557.     }
  558.     else
  559.     {
  560.         /* configure all port bits as output (LCD data and control lines on different ports */
  561.         DDR(LCD_RS_PORT)    |= _BV(LCD_RS_PIN);
  562.         DDR(LCD_RW_PORT)    |= _BV(LCD_RW_PIN);
  563.         DDR(LCD_E_PORT)     |= _BV(LCD_E_PIN);
  564.         DDR(LCD_DATA0_PORT) |= _BV(LCD_DATA0_PIN);
  565.         DDR(LCD_DATA1_PORT) |= _BV(LCD_DATA1_PIN);
  566.         DDR(LCD_DATA2_PORT) |= _BV(LCD_DATA2_PIN);
  567.         DDR(LCD_DATA3_PORT) |= _BV(LCD_DATA3_PIN);
  568.         LED_ON
  569.     }
  570.     delay(16000);        /* wait 16ms or more after power-on       */
  571.    
  572.     /* initial write to lcd is 8bit */
  573.     LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN);  // _BV(LCD_FUNCTION)>>4;
  574.     LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN);  // _BV(LCD_FUNCTION_8BIT)>>4;
  575.     lcd_e_toggle();
  576.     delay(4992);         /* delay, busy flag can't be checked here */
  577.    
  578.     /* repeat last command */
  579.     lcd_e_toggle();      
  580.     delay(64);           /* delay, busy flag can't be checked here */
  581.    
  582.     /* repeat last command a third time */
  583.     lcd_e_toggle();      
  584.     delay(64);           /* delay, busy flag can't be checked here */
  585.  
  586.     /* now configure for 4bit mode */
  587.     LCD_DATA0_PORT &= ~_BV(LCD_DATA0_PIN);   // LCD_FUNCTION_4BIT_1LINE>>4
  588.     lcd_e_toggle();
  589.     delay(64);           /* some displays need this additional delay */
  590.    
  591.     /* from now the LCD only accepts 4 bit I/O, we can use lcd_command() */    
  592. #else
  593.     /*
  594.      * Initialize LCD to 8 bit memory mapped mode
  595.      */
  596.    
  597.     /* enable external SRAM (memory mapped lcd) and one wait state */        
  598.     MCUCR = _BV(SRE) | _BV(SRW);
  599.  
  600.     /* reset LCD */
  601.     delay(16000);                           /* wait 16ms after power-on     */
  602.     lcd_write(LCD_FUNCTION_8BIT_1LINE,0);   /* function set: 8bit interface */                  
  603.     delay(4992);                            /* wait 5ms                     */
  604.     lcd_write(LCD_FUNCTION_8BIT_1LINE,0);   /* function set: 8bit interface */                
  605.     delay(64);                              /* wait 64us                    */
  606.     lcd_write(LCD_FUNCTION_8BIT_1LINE,0);   /* function set: 8bit interface */                
  607.     delay(64);                              /* wait 64us                    */
  608. #endif
  609.  
  610. #if KS0073_4LINES_MODE
  611.     /* Display with KS0073 controller requires special commands for enabling 4 line mode */
  612.     lcd_command(KS0073_EXTENDED_FUNCTION_REGISTER_ON);
  613.     lcd_command(KS0073_4LINES_MODE);
  614.     lcd_command(KS0073_EXTENDED_FUNCTION_REGISTER_OFF);
  615. #else
  616.     lcd_command(LCD_FUNCTION_DEFAULT);      /* function set: display lines  */
  617. #endif
  618.     lcd_command(LCD_DISP_OFF);              /* display off                  */
  619.     lcd_clrscr();                           /* display clear                */
  620.     lcd_command(LCD_MODE_DEFAULT);          /* set entry mode               */
  621.     lcd_command(dispAttr);                  /* display/cursor control       */
  622.  
  623.     // load the custom characters for the bargraph
  624.     lcdLoadCustomChar((uint8_t*)LcdCustomChar,0,0);
  625.     lcdLoadCustomChar((uint8_t*)LcdCustomChar,1,1);
  626.     lcdLoadCustomChar((uint8_t*)LcdCustomChar,2,2);
  627.     lcdLoadCustomChar((uint8_t*)LcdCustomChar,3,3);
  628.     lcdLoadCustomChar((uint8_t*)LcdCustomChar,4,4);
  629.     lcdLoadCustomChar((uint8_t*)LcdCustomChar,5,5);
  630.    
  631. }/* lcd_init */
  632.  
  633. void lcdProgressBar(uint16_t progress, uint16_t maxprogress, uint8_t length){
  634.     uint8_t i;
  635.     uint32_t pixelprogress;
  636.     uint8_t c;
  637.    
  638.     pixelprogress = ((progress*(length*6))/maxprogress);
  639.  
  640.     // print exactly "length" characters
  641.     for(i=0; i<length; i++){
  642.         // check if this is a full block, or partial or empty
  643.         // (u16) cast is needed to avoid sign comparison warning
  644.         if( ((i*(uint16_t)6)+5) > pixelprogress ) {
  645.             // this is a partial or empty block
  646.             if( ((i*(uint16_t)6)) > pixelprogress ){
  647.                 // this is an empty block
  648.                 // use space character?
  649.                 c = 0;
  650.             } else {
  651.                 // this is a partial block
  652.                 c = pixelprogress % 6;
  653.             }
  654.         } else {
  655.             // this is a full block
  656.             c = 5;
  657.         }    
  658.         // write character to display
  659.         lcd_data(c);
  660.     }  
  661. }
  662.  
  663. void lcdLoadCustomChar(uint8_t* lcdCustomCharArray, uint8_t romCharNum, uint8_t lcdCharNum)
  664. {
  665.     register uint8_t i;
  666.     uint8_t saveDDRAMAddr;
  667.  
  668.     // backup the current cursor position
  669.  //   saveDDRAMAddr = lcdControlRead() & 0x7F; TODO: Fix me
  670.  
  671.     // multiply the character index by 8
  672.     lcdCharNum = (lcdCharNum<<3);   // each character occupies 8 bytes
  673.     romCharNum = (romCharNum<<3);   // each character occupies 8 bytes
  674.  
  675.     // copy the 8 bytes into CG (character generator) RAM
  676.     for(i=0; i<8; i++)
  677.     {
  678.         // set CG RAM address
  679.         lcd_command((1<<LCD_CGRAM) | (lcdCharNum+i));
  680.         // write character data
  681.         lcd_data( pgm_read_byte(lcdCustomCharArray+romCharNum+i) );
  682.     }
  683.  
  684.     // restore the previous cursor position
  685.    // lcd_command(1<<LCD_DDRAM | saveDDRAMAddr); todo: fix me
  686.  
  687. }
  688.