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.12  2009/01/17 11:26:37  haraldkipp
00037  * Getting rid of two remaining BSD types in favor of stdint.
00038  * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
00039  *
00040  * Revision 1.11  2008/08/11 06:59:09  haraldkipp
00041  * BSD types replaced by stdint types (feature request #1282721).
00042  *
00043  * Revision 1.10  2008/08/06 12:51:00  haraldkipp
00044  * Added support for Ethernut 5 (AT91SAM9XE reference design).
00045  *
00046  * Revision 1.9  2008/02/15 16:58:41  haraldkipp
00047  * Spport for AT91SAM7SE512 added.
00048  *
00049  * Revision 1.8  2007/10/04 19:57:54  olereinhardt
00050  * Support for SAM7S256 added
00051  *
00052  * Revision 1.7  2007/02/15 16:05:29  haraldkipp
00053  * Port usage is now configurable. Data bits no longer need four consecutive
00054  * port bits. Added delays in read for better reliability with some slow
00055  * displays.
00056  *
00057  * Revision 1.6  2006/10/05 17:11:16  haraldkipp
00058  * Fixes bug #1567813. Should now work after power on and after reset without
00059  * power loss. Many thanks to Klaus-Dieter Sohn.
00060  *
00061  * Revision 1.5  2006/08/31 19:02:25  haraldkipp
00062  * Added support for AT91SAM9260.
00063  * Some displays fail after reset. An additional nibble sent
00064  * during 4-bit initialization seems to fix this. However,
00065  * a user reported that his 3.3V driven LCD now fails during
00066  * power on.
00067  *
00068  * Revision 1.4  2006/07/15 11:15:31  haraldkipp
00069  * Initialization flag removed. It is not required because the driver doesn't
00070  * poll the busy flag during initialization.
00071  * Bug fixed, which let the driver fail to properly initialize displays with
00072  * two lines.
00073  *
00074  * Revision 1.3  2006/06/28 17:23:19  haraldkipp
00075  * Significantly extend delay time to allow running slow 3.3V LCDs with fast
00076  * CPUs. Not a nice fix, but it works.
00077  *
00078  * Revision 1.2  2006/05/15 11:44:06  haraldkipp
00079  * Added delays for more reliable initialization.
00080  *
00081  * Revision 1.1  2006/04/07 13:50:15  haraldkipp
00082  * ARM driver for HD44780 LCD controller added.
00083  *
00084  */
00085 
00086 #include <cfg/arch.h>
00087 #include <cfg/arch/gpio.h>
00088 #include <cfg/lcd.h>
00089 
00090 #if 0
00091 /* Configuration items. */
00092 #define LCD_DATA_LSB    0
00093 #define LCD_ENABLE_BIT  4
00094 #define LCD_RW_BIT      5
00095 #define LCD_REGSEL_BIT  7
00096 
00097 #endif
00098 
00099 #include <stdlib.h>
00100 #include <string.h>
00101 
00102 #include <sys/nutconfig.h>
00103 #include <dev/hd44780.h>
00104 #include <dev/term.h>
00105 #include <sys/timer.h>
00106 
00107 #if !defined(LCD_4x20) && !defined(LCD_4x16)
00108 #if !defined(LCD_2x40) && !defined(LCD_2x20) && !defined(LCD_2x16) && !defined(LCD_2x8)
00109 #if !defined(LCD_1x20) && !defined(LCD_1x16) && !defined(LCD_1x8)
00110 #if !defined(KS0073_CONTROLLER)
00111 #define LCD_2x16
00112 #endif                          /* !KS0073_CONTROLLER */
00113 #endif                          /* !1 line */
00114 #endif                          /* !2 lines */
00115 #endif                          /* !4 lines */
00116 
00117 #ifndef LCD_ROWS
00118 #if defined(LCD_4x20) || defined(LCD_4x16) || defined(KS0073_CONTROLLER)
00119 #define LCD_ROWS    4
00120 #elif defined(LCD_1x20) || defined(LCD_1x16) || defined(LCD_1x8)
00121 #define LCD_ROWS    1
00122 #else
00123 #define LCD_ROWS    2
00124 #endif
00125 #endif                          /* LCD_ROWS */
00126 
00127 #ifndef LCD_COLS
00128 #if defined(LCD_2x40)
00129 #define LCD_COLS    40
00130 #elif defined(LCD_4x20) || defined(LCD_2x20) || defined(LCD_1x20) || defined(KS0073_CONTROLLER)
00131 #define LCD_COLS    20
00132 #elif defined(LCD_2x8) || defined(LCD_1x8)
00133 #define LCD_COLS    8
00134 #else
00135 #define LCD_COLS    16
00136 #endif
00137 #endif                          /* LCD_COLS */
00138 
00142 #if !defined(LCD_PIO_ID)
00143 #if defined(MCU_AT91SAM7X256) || defined (MCU_AT91SAM7S256) || defined (MCU_AT91SAM7SE512)
00144 #define LCD_PIO_ID  PIOA_ID
00145 #elif defined(MCU_AT91SAM9260) || defined(MCU_AT91SAM9XE512)
00146 #define LCD_PIO_ID  PIOB_ID
00147 #else
00148 #define LCD_PIO_ID  PIO_ID
00149 #endif
00150 #endif
00151 
00155 #if !defined(LCD_PIO_PE_REG)
00156 #if LCD_PIO_ID == PIOA_ID
00157 #define LCD_PIO_PE_REG  PIOA_PER
00158 #elif LCD_PIO_ID == PIOB_ID
00159 #define LCD_PIO_PE_REG  PIOB_PER
00160 #elif LCD_PIO_ID == PIOC_ID
00161 #define LCD_PIO_PE_REG  PIOC_PER
00162 #else
00163 #define LCD_PIO_PE_REG  PIO_PER
00164 #endif
00165 #endif
00166 
00170 #if !defined(LCD_PIO_OE_REG)
00171 #if LCD_PIO_ID == PIOA_ID
00172 #define LCD_PIO_OE_REG  PIOA_OER
00173 #elif LCD_PIO_ID == PIOB_ID
00174 #define LCD_PIO_OE_REG  PIOB_OER
00175 #elif LCD_PIO_ID == PIOC_ID
00176 #define LCD_PIO_OE_REG  PIOC_OER
00177 #else
00178 #define LCD_PIO_OE_REG  PIO_OER
00179 #endif
00180 #endif
00181 
00185 #ifdef LCD_RW_BIT
00186 #if !defined(LCD_PIO_OD_REG)
00187 #if LCD_PIO_ID == PIOA_ID
00188 #define LCD_PIO_OD_REG  PIOA_ODR
00189 #elif LCD_PIO_ID == PIOB_ID
00190 #define LCD_PIO_OD_REG  PIOB_ODR
00191 #elif LCD_PIO_ID == PIOC_ID
00192 #define LCD_PIO_OD_REG  PIOC_ODR
00193 #else
00194 #define LCD_PIO_OD_REG  PIO_ODR
00195 #endif
00196 #endif
00197 #endif /* LCD_RW_BIT */
00198 
00202 #if !defined(LCD_PIO_SOD_REG)
00203 #if LCD_PIO_ID == PIOA_ID
00204 #define LCD_PIO_SOD_REG PIOA_SODR
00205 #elif LCD_PIO_ID == PIOB_ID
00206 #define LCD_PIO_SOD_REG PIOB_SODR
00207 #elif LCD_PIO_ID == PIOC_ID
00208 #define LCD_PIO_SOD_REG PIOC_SODR
00209 #else
00210 #define LCD_PIO_SOD_REG PIO_SODR
00211 #endif
00212 #endif
00213 
00217 #if !defined(LCD_PIO_COD_REG)
00218 #if LCD_PIO_ID == PIOA_ID
00219 #define LCD_PIO_COD_REG PIOA_CODR
00220 #elif LCD_PIO_ID == PIOB_ID
00221 #define LCD_PIO_COD_REG PIOB_CODR
00222 #elif LCD_PIO_ID == PIOC_ID
00223 #define LCD_PIO_COD_REG PIOC_CODR
00224 #else
00225 #define LCD_PIO_COD_REG PIO_CODR
00226 #endif
00227 #endif
00228 
00232 #ifdef LCD_RW_BIT
00233 #if !defined(LCD_PIO_PDS_REG)
00234 #if LCD_PIO_ID == PIOA_ID
00235 #define LCD_PIO_PDS_REG PIOA_PDSR
00236 #elif LCD_PIO_ID == PIOB_ID
00237 #define LCD_PIO_PDS_REG PIOB_PDSR
00238 #elif LCD_PIO_ID == PIOC_ID
00239 #define LCD_PIO_PDS_REG PIOC_PDSR
00240 #else
00241 #define LCD_PIO_PDS_REG PIO_PDSR
00242 #endif
00243 #endif
00244 #endif /* LCD_RW_BIT */
00245 
00246 #if !defined(LCD_DATA_LSB) && !defined(LCD_DATA_BIT0)
00247 #define LCD_DATA_LSB    0
00248 #endif
00249 
00250 #ifdef LCD_DATA_LSB
00251 #define LCD_DATA    (0xF << LCD_DATA_LSB)
00252 #else
00253 #define LCD_D0      _BV(LCD_DATA_BIT0)
00254 #define LCD_D1      _BV(LCD_DATA_BIT1)
00255 #define LCD_D2      _BV(LCD_DATA_BIT2)
00256 #define LCD_D3      _BV(LCD_DATA_BIT3)
00257 #define LCD_DATA    (LCD_D0 | LCD_D1 | LCD_D2 | LCD_D3)
00258 #endif
00259 
00260 #ifndef LCD_ENABLE_BIT
00261 #define LCD_ENABLE_BIT  4
00262 #endif
00263 #define LCD_EN      _BV(LCD_ENABLE_BIT)
00264 
00265 #ifndef LCD_REGSEL_BIT
00266 #define LCD_REGSEL_BIT  7
00267 #endif
00268 #define LCD_RS      _BV(LCD_REGSEL_BIT)
00269 
00270 #ifdef LCD_RW_BIT
00271 #define LCD_RW      _BV(LCD_RW_BIT)
00272 #endif
00273 
00274 #ifndef LCD_SHORT_DELAY
00275 #define LCD_SHORT_DELAY 10
00276 #endif
00277 
00278 #ifndef LCD_LONG_DELAY
00279 #define LCD_LONG_DELAY  1000
00280 #endif
00281 
00286 
00297 static void LcdDelay(unsigned int cycles)
00298 {
00299     while (cycles--) {
00300         _NOP(); _NOP(); _NOP(); _NOP();
00301         _NOP(); _NOP(); _NOP(); _NOP();
00302         _NOP(); _NOP(); _NOP(); _NOP();
00303         _NOP(); _NOP(); _NOP(); _NOP();
00304         _NOP(); _NOP(); _NOP(); _NOP();
00305         _NOP(); _NOP(); _NOP(); _NOP();
00306         _NOP(); _NOP(); _NOP(); _NOP();
00307         _NOP(); _NOP(); _NOP(); _NOP();
00308         _NOP(); _NOP(); _NOP(); _NOP();
00309         _NOP(); _NOP(); _NOP(); _NOP();
00310         _NOP(); _NOP(); _NOP(); _NOP();
00311         _NOP(); _NOP(); _NOP(); _NOP();
00312     }
00313 }
00314 
00315 static void INLINE LcdSetBits(unsigned int mask)
00316 {
00317     outr(LCD_PIO_SOD_REG, mask);
00318     outr(LCD_PIO_OE_REG, mask);
00319 }
00320 
00321 static void INLINE LcdClrBits(unsigned int mask)
00322 {
00323     outr(LCD_PIO_COD_REG, mask);
00324     outr(LCD_PIO_OE_REG, mask);
00325 }
00326 
00327 #ifdef LCD_RW_BIT
00328 
00329 static unsigned int LcdReadNibble(void)
00330 {
00331     unsigned int rc;
00332 
00333     LcdSetBits(LCD_EN);
00334     LcdDelay(LCD_SHORT_DELAY);
00335     rc = inr(LCD_PIO_PDS_REG) & LCD_DATA;
00336     LcdClrBits(LCD_EN);
00337     LcdDelay(LCD_SHORT_DELAY);
00338 
00339 #ifdef LCD_DATA_LSB
00340     rc >>= LCD_DATA_LSB
00341 #else
00342     {
00343         unsigned int val = 0;
00344 
00345         if (rc & LCD_D0) {
00346             val |= 0x01;
00347         }
00348         if (rc & LCD_D1) {
00349             val |= 0x02;
00350         }
00351         if (rc & LCD_D2) {
00352             val |= 0x04;
00353         }
00354         if (rc & LCD_D3) {
00355             val |= 0x08;
00356         }
00357         rc = val;
00358     }
00359 #endif
00360     return rc;
00361 }
00362 
00366 static unsigned int LcdReadByte(void)
00367 {
00368     outr(LCD_PIO_OD_REG, LCD_DATA);
00369     LcdDelay(LCD_SHORT_DELAY);
00370     LcdSetBits(LCD_RW);
00371     LcdDelay(LCD_SHORT_DELAY);
00372     return (LcdReadNibble() << 4) | LcdReadNibble();
00373 }
00374 
00378 static unsigned int LcdReadStatus(void)
00379 {
00380     /* RS low selects status register. */
00381     LcdClrBits(LCD_RS);
00382     return LcdReadByte();
00383 }
00384 
00385 #endif                          /* LCD_RW_BIT */
00386 
00387 static void LcdWaitReady(unsigned int delay)
00388 {
00389     while (delay--) {
00390 #if defined(LCD_RW_BIT)
00391         if ((LcdReadStatus() & _BV(LCD_BUSY)) == 0) {
00392             break;
00393         }
00394 #endif
00395         _NOP();
00396     }
00397 }
00398 
00404 static void LcdWriteNibble(unsigned int nib)
00405 {
00406 #ifdef LCD_DATA_LSB
00407     nib <<= LCD_DATA_LSB;
00408 #else
00409     {
00410         unsigned int val = 0;
00411         if (nib & 0x01) {
00412             val |= LCD_D0;
00413         }
00414         if (nib & 0x02) {
00415             val |= LCD_D1;
00416         }
00417         if (nib & 0x04) {
00418             val |= LCD_D2;
00419         }
00420         if (nib & 0x08) {
00421             val |= LCD_D3;
00422         }
00423         nib = val;
00424     }
00425 #endif
00426     LcdSetBits(nib & LCD_DATA);
00427     LcdClrBits(~nib & LCD_DATA);
00428 
00429     LcdDelay(LCD_SHORT_DELAY);
00430     LcdSetBits(LCD_EN);
00431     LcdDelay(LCD_SHORT_DELAY);
00432     LcdClrBits(LCD_EN);
00433     LcdDelay(LCD_SHORT_DELAY);
00434 }
00435 
00441 static void LcdWriteByte(unsigned int data)
00442 {
00443 #ifdef LCD_RW_BIT
00444     LcdClrBits(LCD_RW);
00445 #endif
00446     LcdWriteNibble(data >> 4);
00447     LcdWriteNibble(data);
00448     LcdWaitReady(LCD_LONG_DELAY);
00449 }
00450 
00456 static void LcdWriteCmd(uint8_t cmd)
00457 {
00458     /* RS low selects instruction register. */
00459     LcdClrBits(LCD_RS);
00460     LcdWriteByte(cmd);
00461 }
00462 
00463 static void LcdWriteInstruction(uint8_t cmd, uint8_t xt)
00464 {
00465     LcdWriteCmd(cmd);
00466 }
00467 
00473 static void LcdWriteData(uint8_t data)
00474 {
00475     /* RS high selects data register. */
00476     LcdSetBits(LCD_RS);
00477     LcdWriteByte(data);
00478 }
00479 
00480 static void LcdSetCursor(uint8_t pos)
00481 {
00482     uint8_t offset[] = {
00483 #ifdef KS0073_CONTROLLER
00484         0x00, 0x20, 0x40, 0x60
00485 #elif LCD_COLS == 20
00486         0x00, 0x40, 0x14, 0x54
00487 #else
00488         0x00, 0x40, 0x10, 0x50
00489 #endif
00490     };
00491 
00492     pos = offset[(pos / LCD_COLS) % LCD_ROWS] + pos % LCD_COLS;
00493     LcdWriteCmd(1 << LCD_DDRAM | pos);
00494 }
00495 
00496 static void LcdCursorHome(void)
00497 {
00498     LcdWriteCmd(1 << LCD_HOME);
00499     LcdDelay(10 * LCD_LONG_DELAY);
00500 }
00501 
00502 static void LcdCursorLeft(void)
00503 {
00504     LcdWriteCmd(1 << LCD_MOVE);
00505 }
00506 
00507 static void LcdCursorRight(void)
00508 {
00509     LcdWriteCmd(1 << LCD_MOVE | 1 << LCD_MOVE_RIGHT);
00510 }
00511 
00512 static void LcdClear(void)
00513 {
00514     LcdWriteCmd(_BV(LCD_CLR));
00515     LcdDelay(10 * LCD_LONG_DELAY);
00516 }
00517 
00518 static void LcdCursorMode(uint8_t on)
00519 {
00520     LcdWriteCmd(1 << LCD_ON_CTRL | on ? 1 << LCD_ON_CURSOR : 0x00);
00521     LcdDelay(10 * LCD_LONG_DELAY);
00522 }
00523 
00524 static void LcdInit(NUTDEVICE * dev)
00525 {
00526 #if defined(PMC_PCER)
00527     outr(PMC_PCER, _BV(LCD_PIO_ID));
00528 #endif
00529 
00530     /* Initialize GPIO lines. */
00531 #ifdef LCD_RW_BIT
00532     outr(LCD_PIO_PE_REG, LCD_RW);
00533     LcdClrBits(LCD_RW);
00534 #endif
00535     outr(LCD_PIO_PE_REG, LCD_EN | LCD_RS | LCD_DATA);
00536     LcdClrBits(LCD_DATA | LCD_RS);
00537     LcdDelay(LCD_LONG_DELAY);
00538     LcdClrBits(LCD_EN);
00539     LcdDelay(LCD_LONG_DELAY);
00540 
00541     /* Initial delay. Actually only required after power on. */
00542     NutSleep(16);
00543 
00544     /* This initialization will make sure, that the LCD is switched
00545        to 4-bit mode, no matter which mode we start from. */
00546     LcdWriteNibble((_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_8BIT)) >> 4);
00547     NutSleep(5);
00548     LcdWriteNibble((_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_8BIT)) >> 4);
00549     NutSleep(2);
00550     LcdWriteNibble((_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_8BIT)) >> 4);
00551     NutSleep(2);
00552     LcdWriteNibble(_BV(LCD_FUNCTION) >> 4);
00553     NutSleep(2);
00554 
00555     /* Set number of lines and font. Can't be changed later. */
00556     LcdWriteNibble((_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_2LINES)) >> 4);
00557     LcdWriteNibble(_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_2LINES));
00558     NutSleep(2);
00559 
00560     /* Switch display and cursor off. */
00561     LcdWriteNibble(_BV(LCD_ON_CTRL) >> 4);
00562     LcdWriteNibble(_BV(LCD_ON_CTRL));
00563     NutSleep(2);
00564 
00565     /* Clear display. */
00566     LcdClear();
00567 
00568     /* Set entry mode. */
00569     LcdWriteCmd(_BV(LCD_ENTRY_MODE) | _BV(LCD_ENTRY_INC));
00570     /* Switch display on. */
00571     LcdWriteCmd(_BV(LCD_ON_CTRL) | _BV(LCD_ON_DISPLAY));
00572     /* Move cursor home. */
00573     LcdCursorHome();
00574     /* Set data address to zero. */
00575     LcdWriteCmd(_BV(LCD_DDRAM));
00576 }
00577 
00581 TERMDCB dcb_term = {
00582     LcdInit,                    
00583     LcdWriteData,               
00584     LcdWriteInstruction,        
00585     LcdClear,                   
00586     LcdSetCursor,               
00587     LcdCursorHome,              
00588     LcdCursorLeft,              
00589     LcdCursorRight,             
00590     LcdCursorMode,              
00591     0,                          
00592     0,                          
00593     LCD_ROWS,                   
00594     LCD_COLS,                   
00595     LCD_COLS,                   
00596     0,                          
00597     0,                          
00598     0                           
00599 };
00600 
00604 NUTDEVICE devLcd = {
00605     0,                          
00606     {'l', 'c', 'd', 0, 0, 0, 0, 0, 0},  
00607     IFTYP_STREAM,               
00608     0,                          
00609     0,                          
00610     0,                          
00611     &dcb_term,                  
00612     TermInit,                   
00613     TermIOCtl,                  
00614     0,
00615     TermWrite,
00616     TermOpen,
00617     TermClose,
00618     0
00619 };
00620 

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