hd44780_at91.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2007 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00034 /*
00035  * $Log: hd44780_at91.c,v $
00036  * Revision 1.7  2007/02/15 16:05:29  haraldkipp
00037  * Port usage is now configurable. Data bits no longer need four consecutive
00038  * port bits. Added delays in read for better reliability with some slow
00039  * displays.
00040  *
00041  * Revision 1.6  2006/10/05 17:11:16  haraldkipp
00042  * Fixes bug #1567813. Should now work after power on and after reset without
00043  * power loss. Many thanks to Klaus-Dieter Sohn.
00044  *
00045  * Revision 1.5  2006/08/31 19:02:25  haraldkipp
00046  * Added support for AT91SAM9260.
00047  * Some displays fail after reset. An additional nibble sent
00048  * during 4-bit initialization seems to fix this. However,
00049  * a user reported that his 3.3V driven LCD now fails during
00050  * power on.
00051  *
00052  * Revision 1.4  2006/07/15 11:15:31  haraldkipp
00053  * Initialization flag removed. It is not required because the driver doesn't
00054  * poll the busy flag during initialization.
00055  * Bug fixed, which let the driver fail to properly initialize displays with
00056  * two lines.
00057  *
00058  * Revision 1.3  2006/06/28 17:23:19  haraldkipp
00059  * Significantly extend delay time to allow running slow 3.3V LCDs with fast
00060  * CPUs. Not a nice fix, but it works.
00061  *
00062  * Revision 1.2  2006/05/15 11:44:06  haraldkipp
00063  * Added delays for more reliable initialization.
00064  *
00065  * Revision 1.1  2006/04/07 13:50:15  haraldkipp
00066  * ARM driver for HD44780 LCD controller added.
00067  *
00068  */
00069 
00070 #include <cfg/arch.h>
00071 #include <cfg/arch/gpio.h>
00072 #include <cfg/lcd.h>
00073 
00074 #if 0
00075 /* Configuration items. */
00076 #define LCD_DATA_LSB    0
00077 #define LCD_ENABLE_BIT  4
00078 #define LCD_RW_BIT      5
00079 #define LCD_REGSEL_BIT  7
00080 
00081 #endif
00082 
00083 #include <stdlib.h>
00084 #include <string.h>
00085 
00086 #include <sys/nutconfig.h>
00087 #include <dev/hd44780.h>
00088 #include <dev/term.h>
00089 #include <sys/timer.h>
00090 
00091 #if !defined(LCD_4x20) && !defined(LCD_4x16)
00092 #if !defined(LCD_2x40) && !defined(LCD_2x20) && !defined(LCD_2x16) && !defined(LCD_2x8)
00093 #if !defined(LCD_1x20) && !defined(LCD_1x16) && !defined(LCD_1x8)
00094 #if !defined(KS0073_CONTROLLER)
00095 #define LCD_2x16
00096 #endif                          /* !KS0073_CONTROLLER */
00097 #endif                          /* !1 line */
00098 #endif                          /* !2 lines */
00099 #endif                          /* !4 lines */
00100 
00101 #ifndef LCD_ROWS
00102 #if defined(LCD_4x20) || defined(LCD_4x16) || defined(KS0073_CONTROLLER)
00103 #define LCD_ROWS    4
00104 #elif defined(LCD_1x20) || defined(LCD_1x16) || defined(LCD_1x8)
00105 #define LCD_ROWS    1
00106 #else
00107 #define LCD_ROWS    2
00108 #endif
00109 #endif                          /* LCD_ROWS */
00110 
00111 #ifndef LCD_COLS
00112 #if defined(LCD_2x40)
00113 #define LCD_COLS    40
00114 #elif defined(LCD_4x20) || defined(LCD_2x20) || defined(LCD_1x20) || defined(KS0073_CONTROLLER)
00115 #define LCD_COLS    20
00116 #elif defined(LCD_2x8) || defined(LCD_1x8)
00117 #define LCD_COLS    8
00118 #else
00119 #define LCD_COLS    16
00120 #endif
00121 #endif                          /* LCD_COLS */
00122 
00126 #if !defined(LCD_PIO_ID)
00127 #if defined(MCU_AT91SAM7X256)
00128 #define LCD_PIO_ID  PIOA_ID
00129 #elif defined(MCU_AT91SAM9260)
00130 #define LCD_PIO_ID  PIOB_ID
00131 #else
00132 #define LCD_PIO_ID  PIO_ID
00133 #endif
00134 #endif
00135 
00139 #if !defined(LCD_PIO_PE_REG)
00140 #if LCD_PIO_ID == PIOA_ID
00141 #define LCD_PIO_PE_REG  PIOA_PER
00142 #elif LCD_PIO_ID == PIOB_ID
00143 #define LCD_PIO_PE_REG  PIOB_PER
00144 #elif LCD_PIO_ID == PIOC_ID
00145 #define LCD_PIO_PE_REG  PIOC_PER
00146 #else
00147 #define LCD_PIO_PE_REG  PIO_PER
00148 #endif
00149 #endif
00150 
00154 #if !defined(LCD_PIO_OE_REG)
00155 #if LCD_PIO_ID == PIOA_ID
00156 #define LCD_PIO_OE_REG  PIOA_OER
00157 #elif LCD_PIO_ID == PIOB_ID
00158 #define LCD_PIO_OE_REG  PIOB_OER
00159 #elif LCD_PIO_ID == PIOC_ID
00160 #define LCD_PIO_OE_REG  PIOC_OER
00161 #else
00162 #define LCD_PIO_OE_REG  PIO_OER
00163 #endif
00164 #endif
00165 
00169 #ifdef LCD_RW_BIT
00170 #if !defined(LCD_PIO_OD_REG)
00171 #if LCD_PIO_ID == PIOA_ID
00172 #define LCD_PIO_OD_REG  PIOA_ODR
00173 #elif LCD_PIO_ID == PIOB_ID
00174 #define LCD_PIO_OD_REG  PIOB_ODR
00175 #elif LCD_PIO_ID == PIOC_ID
00176 #define LCD_PIO_OD_REG  PIOC_ODR
00177 #else
00178 #define LCD_PIO_OD_REG  PIO_ODR
00179 #endif
00180 #endif
00181 #endif /* LCD_RW_BIT */
00182 
00186 #if !defined(LCD_PIO_SOD_REG)
00187 #if LCD_PIO_ID == PIOA_ID
00188 #define LCD_PIO_SOD_REG PIOA_SODR
00189 #elif LCD_PIO_ID == PIOB_ID
00190 #define LCD_PIO_SOD_REG PIOB_SODR
00191 #elif LCD_PIO_ID == PIOC_ID
00192 #define LCD_PIO_SOD_REG PIOC_SODR
00193 #else
00194 #define LCD_PIO_SOD_REG PIO_SODR
00195 #endif
00196 #endif
00197 
00201 #if !defined(LCD_PIO_COD_REG)
00202 #if LCD_PIO_ID == PIOA_ID
00203 #define LCD_PIO_COD_REG PIOA_CODR
00204 #elif LCD_PIO_ID == PIOB_ID
00205 #define LCD_PIO_COD_REG PIOB_CODR
00206 #elif LCD_PIO_ID == PIOC_ID
00207 #define LCD_PIO_COD_REG PIOC_CODR
00208 #else
00209 #define LCD_PIO_COD_REG PIO_CODR
00210 #endif
00211 #endif
00212 
00216 #ifdef LCD_RW_BIT
00217 #if !defined(LCD_PIO_PDS_REG)
00218 #if LCD_PIO_ID == PIOA_ID
00219 #define LCD_PIO_PDS_REG PIOA_PDSR
00220 #elif LCD_PIO_ID == PIOB_ID
00221 #define LCD_PIO_PDS_REG PIOB_PDSR
00222 #elif LCD_PIO_ID == PIOC_ID
00223 #define LCD_PIO_PDS_REG PIOC_PDSR
00224 #else
00225 #define LCD_PIO_PDS_REG PIO_PDSR
00226 #endif
00227 #endif
00228 #endif /* LCD_RW_BIT */
00229 
00230 #if !defined(LCD_DATA_LSB) && !defined(LCD_DATA_BIT0)
00231 #define LCD_DATA_LSB    0
00232 #endif
00233 
00234 #ifdef LCD_DATA_LSB
00235 #define LCD_DATA    (0xF << LCD_DATA_LSB)
00236 #else
00237 #define LCD_D0      _BV(LCD_DATA_BIT0)
00238 #define LCD_D1      _BV(LCD_DATA_BIT1)
00239 #define LCD_D2      _BV(LCD_DATA_BIT2)
00240 #define LCD_D3      _BV(LCD_DATA_BIT3)
00241 #define LCD_DATA    (LCD_D0 | LCD_D1 | LCD_D2 | LCD_D3)
00242 #endif
00243 
00244 #ifndef LCD_ENABLE_BIT
00245 #define LCD_ENABLE_BIT  4
00246 #endif
00247 #define LCD_EN      _BV(LCD_ENABLE_BIT)
00248 
00249 #ifndef LCD_REGSEL_BIT
00250 #define LCD_REGSEL_BIT  7
00251 #endif
00252 #define LCD_RS      _BV(LCD_REGSEL_BIT)
00253 
00254 #ifdef LCD_RW_BIT
00255 #define LCD_RW      _BV(LCD_RW_BIT)
00256 #endif
00257 
00258 #ifndef LCD_SHORT_DELAY
00259 #define LCD_SHORT_DELAY 10
00260 #endif
00261 
00262 #ifndef LCD_LONG_DELAY
00263 #define LCD_LONG_DELAY  1000
00264 #endif
00265 
00270 
00281 static void LcdDelay(u_int cycles)
00282 {
00283     while (cycles--) {
00284         _NOP(); _NOP(); _NOP(); _NOP();
00285         _NOP(); _NOP(); _NOP(); _NOP();
00286         _NOP(); _NOP(); _NOP(); _NOP();
00287         _NOP(); _NOP(); _NOP(); _NOP();
00288         _NOP(); _NOP(); _NOP(); _NOP();
00289         _NOP(); _NOP(); _NOP(); _NOP();
00290         _NOP(); _NOP(); _NOP(); _NOP();
00291         _NOP(); _NOP(); _NOP(); _NOP();
00292         _NOP(); _NOP(); _NOP(); _NOP();
00293         _NOP(); _NOP(); _NOP(); _NOP();
00294         _NOP(); _NOP(); _NOP(); _NOP();
00295         _NOP(); _NOP(); _NOP(); _NOP();
00296     }
00297 }
00298 
00299 static void INLINE LcdSetBits(u_int mask)
00300 {
00301     outr(LCD_PIO_SOD_REG, mask);
00302     outr(LCD_PIO_OE_REG, mask);
00303 }
00304 
00305 static void INLINE LcdClrBits(u_int mask)
00306 {
00307     outr(LCD_PIO_COD_REG, mask);
00308     outr(LCD_PIO_OE_REG, mask);
00309 }
00310 
00311 #ifdef LCD_RW_BIT
00312 
00313 static u_int LcdReadNibble(void)
00314 {
00315     u_int rc;
00316 
00317     LcdSetBits(LCD_EN);
00318     LcdDelay(LCD_SHORT_DELAY);
00319     rc = inr(LCD_PIO_PDS_REG) & LCD_DATA;
00320     LcdClrBits(LCD_EN);
00321     LcdDelay(LCD_SHORT_DELAY);
00322 
00323 #ifdef LCD_DATA_LSB
00324     rc >>= LCD_DATA_LSB
00325 #else
00326     {
00327         u_int val = 0;
00328 
00329         if (rc & LCD_D0) {
00330             val |= 0x01;
00331         }
00332         if (rc & LCD_D1) {
00333             val |= 0x02;
00334         }
00335         if (rc & LCD_D2) {
00336             val |= 0x04;
00337         }
00338         if (rc & LCD_D3) {
00339             val |= 0x08;
00340         }
00341         rc = val;
00342     }
00343 #endif
00344     return rc;
00345 }
00346 
00350 static u_int LcdReadByte(void)
00351 {
00352     outr(LCD_PIO_OD_REG, LCD_DATA);
00353     LcdDelay(LCD_SHORT_DELAY);
00354     LcdSetBits(LCD_RW);
00355     LcdDelay(LCD_SHORT_DELAY);
00356     return (LcdReadNibble() << 4) | LcdReadNibble();
00357 }
00358 
00362 static u_int LcdReadStatus(void)
00363 {
00364     /* RS low selects status register. */
00365     LcdClrBits(LCD_RS);
00366     return LcdReadByte();
00367 }
00368 
00369 #endif                          /* LCD_RW_BIT */
00370 
00371 static void LcdWaitReady(u_int delay)
00372 {
00373     while (delay--) {
00374 #if defined(LCD_RW_BIT)
00375         if ((LcdReadStatus() & _BV(LCD_BUSY)) == 0) {
00376             break;
00377         }
00378 #endif
00379         _NOP();
00380     }
00381 }
00382 
00388 static void LcdWriteNibble(u_int nib)
00389 {
00390 #ifdef LCD_DATA_LSB
00391     nib <<= LCD_DATA_LSB;
00392 #else
00393     {
00394         u_int val = 0;
00395         if (nib & 0x01) {
00396             val |= LCD_D0;
00397         }
00398         if (nib & 0x02) {
00399             val |= LCD_D1;
00400         }
00401         if (nib & 0x04) {
00402             val |= LCD_D2;
00403         }
00404         if (nib & 0x08) {
00405             val |= LCD_D3;
00406         }
00407         nib = val;
00408     }
00409 #endif
00410     LcdSetBits(nib & LCD_DATA);
00411     LcdClrBits(~nib & LCD_DATA);
00412 
00413     LcdDelay(LCD_SHORT_DELAY);
00414     LcdSetBits(LCD_EN);
00415     LcdDelay(LCD_SHORT_DELAY);
00416     LcdClrBits(LCD_EN);
00417     LcdDelay(LCD_SHORT_DELAY);
00418 }
00419 
00425 static void LcdWriteByte(u_int data)
00426 {
00427 #ifdef LCD_RW_BIT
00428     LcdClrBits(LCD_RW);
00429 #endif
00430     LcdWriteNibble(data >> 4);
00431     LcdWriteNibble(data);
00432     LcdWaitReady(LCD_LONG_DELAY);
00433 }
00434 
00440 static void LcdWriteCmd(u_char cmd)
00441 {
00442     /* RS low selects instruction register. */
00443     LcdClrBits(LCD_RS);
00444     LcdWriteByte(cmd);
00445 }
00446 
00447 static void LcdWriteInstruction(u_char cmd, u_char xt)
00448 {
00449     LcdWriteCmd(cmd);
00450 }
00451 
00457 static void LcdWriteData(u_char data)
00458 {
00459     /* RS high selects data register. */
00460     LcdSetBits(LCD_RS);
00461     LcdWriteByte(data);
00462 }
00463 
00464 static void LcdSetCursor(u_char pos)
00465 {
00466     u_char offset[] = {
00467 #ifdef KS0073_CONTROLLER
00468         0x00, 0x20, 0x40, 0x60
00469 #elif LCD_COLS == 20
00470         0x00, 0x40, 0x14, 0x54
00471 #else
00472         0x00, 0x40, 0x10, 0x50
00473 #endif
00474     };
00475 
00476     pos = offset[(pos / LCD_COLS) % LCD_ROWS] + pos % LCD_COLS;
00477     LcdWriteCmd(1 << LCD_DDRAM | pos);
00478 }
00479 
00480 static void LcdCursorHome(void)
00481 {
00482     LcdWriteCmd(1 << LCD_HOME);
00483     LcdDelay(10 * LCD_LONG_DELAY);
00484 }
00485 
00486 static void LcdCursorLeft(void)
00487 {
00488     LcdWriteCmd(1 << LCD_MOVE);
00489 }
00490 
00491 static void LcdCursorRight(void)
00492 {
00493     LcdWriteCmd(1 << LCD_MOVE | 1 << LCD_MOVE_RIGHT);
00494 }
00495 
00496 static void LcdClear(void)
00497 {
00498     LcdWriteCmd(_BV(LCD_CLR));
00499     LcdDelay(10 * LCD_LONG_DELAY);
00500 }
00501 
00502 static void LcdCursorMode(u_char on)
00503 {
00504     LcdWriteCmd(1 << LCD_ON_CTRL | on ? 1 << LCD_ON_CURSOR : 0x00);
00505     LcdDelay(10 * LCD_LONG_DELAY);
00506 }
00507 
00508 static void LcdInit(NUTDEVICE * dev)
00509 {
00510 #if defined(PMC_PCER)
00511     outr(PMC_PCER, _BV(LCD_PIO_ID));
00512 #endif
00513 
00514     /* Initialize GPIO lines. */
00515 #ifdef LCD_RW_BIT
00516     outr(LCD_PIO_PE_REG, LCD_RW);
00517     LcdClrBits(LCD_RW);
00518 #endif
00519     outr(LCD_PIO_PE_REG, LCD_EN | LCD_RS | LCD_DATA);
00520     LcdClrBits(LCD_DATA | LCD_RS);
00521     LcdDelay(LCD_LONG_DELAY);
00522     LcdClrBits(LCD_EN);
00523     LcdDelay(LCD_LONG_DELAY);
00524 
00525     /* Initial delay. Actually only required after power on. */
00526     NutSleep(16);
00527 
00528     /* This initialization will make sure, that the LCD is switched
00529        to 4-bit mode, no matter which mode we start from. */
00530     LcdWriteNibble((_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_8BIT)) >> 4);
00531     NutSleep(5);
00532     LcdWriteNibble((_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_8BIT)) >> 4);
00533     NutSleep(2);
00534     LcdWriteNibble((_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_8BIT)) >> 4);
00535     NutSleep(2);
00536     LcdWriteNibble(_BV(LCD_FUNCTION) >> 4);
00537     NutSleep(2);
00538 
00539     /* Set number of lines and font. Can't be changed later. */
00540     LcdWriteNibble((_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_2LINES)) >> 4);
00541     LcdWriteNibble(_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_2LINES));
00542     NutSleep(2);
00543 
00544     /* Switch display and cursor off. */
00545     LcdWriteNibble(_BV(LCD_ON_CTRL) >> 4);
00546     LcdWriteNibble(_BV(LCD_ON_CTRL));
00547     NutSleep(2);
00548 
00549     /* Clear display. */
00550     LcdClear();
00551 
00552     /* Set entry mode. */
00553     LcdWriteCmd(_BV(LCD_ENTRY_MODE) | _BV(LCD_ENTRY_INC));
00554     /* Switch display on. */
00555     LcdWriteCmd(_BV(LCD_ON_CTRL) | _BV(LCD_ON_DISPLAY));
00556     /* Move cursor home. */
00557     LcdCursorHome();
00558     /* Set data address to zero. */
00559     LcdWriteCmd(_BV(LCD_DDRAM));
00560 }
00561 
00565 TERMDCB dcb_term = {
00566     LcdInit,                    
00567     LcdWriteData,               
00568     LcdWriteInstruction,        
00569     LcdClear,                   
00570     LcdSetCursor,               
00571     LcdCursorHome,              
00572     LcdCursorLeft,              
00573     LcdCursorRight,             
00574     LcdCursorMode,              
00575     0,                          
00576     0,                          
00577     LCD_ROWS,                   
00578     LCD_COLS,                   
00579     LCD_COLS,                   
00580     0,                          
00581     0,                          
00582     0                           
00583 };
00584 
00588 NUTDEVICE devLcd = {
00589     0,                          
00590     {'l', 'c', 'd', 0, 0, 0, 0, 0, 0},  
00591     IFTYP_STREAM,               
00592     0,                          
00593     0,                          
00594     0,                          
00595     &dcb_term,                  
00596     TermInit,                   
00597     TermIOCtl,                  
00598     0,
00599     TermWrite,
00600     TermOpen,
00601     TermClose,
00602     0
00603 };
00604 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/