Nut/OS  4.10.3
API Reference
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$
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/armpio.h>
00088 #include <cfg/lcd.h>
00089 
00090 #include <stdlib.h>
00091 #include <string.h>
00092 //#include <stdio.h>
00093 
00094 #include <sys/nutconfig.h>
00095 #include <dev/hd44780.h>
00096 #include <dev/term.h>
00097 #include <sys/timer.h>
00098 
00099 #ifdef LCD_IF_4BIT
00100 #ifdef LCD_DATA_LSB
00101 #define LCD_DATA    (0xF << LCD_DATA_LSB)
00102 #else   /* LCD_DATA_LSB */
00103 #define LCD_D0      _BV(LCD_DATA_BIT0)
00104 #define LCD_D1      _BV(LCD_DATA_BIT1)
00105 #define LCD_D2      _BV(LCD_DATA_BIT2)
00106 #define LCD_D3      _BV(LCD_DATA_BIT3)
00107 #define LCD_DATA    (LCD_D0 | LCD_D1 | LCD_D2 | LCD_D3)
00108 #endif  /* LCD_DATA_LSB */
00109 #else   /* LCD_IF_4BIT */
00110 #define LCD_DATA     (0xFF << LCD_DATA_LSB)
00111 #endif  /* LCD_IF_4BIT */
00112 
00117 
00129 #ifdef LCD_PW_EH
00130 void LcdNanoDelay( uint_fast16_t n)
00131 {
00132     while (n--) {
00133         _NOP();
00134     }
00135 }
00136 #else
00137 #define LcdNanoDelay( a)
00138 #endif
00139 
00140 
00141 static void INLINE LcdSetBits(unsigned int mask)
00142 {
00143     outr(LCD_DATA_BASE+PIO_SODR_OFF, mask);
00144     outr(LCD_DATA_BASE+PIO_OER_OFF, mask);
00145 }
00146 
00147 static void INLINE LcdClrBits(unsigned int mask)
00148 {
00149     outr(LCD_DATA_BASE+PIO_CODR_OFF, mask);
00150     outr(LCD_DATA_BASE+PIO_OER_OFF, mask);
00151 }
00152 
00153 #ifdef LCD_RW_BIT
00154 static unsigned int LcdReadNibble(void)
00155 {
00156     unsigned int rc;
00157 
00158     LCD_EN_SET();
00159     LcdDelay(LCD_SHORT_DELAY);
00160     rc = inr(LCD_DATA_PIO_ID+PIO_PDSR_OFF) & LCD_DATA;
00161     LCD_EN_CLR();
00162     LcdDelay(LCD_SHORT_DELAY);
00163 
00164 #ifdef LCD_DATA_LSB
00165     rc >>= LCD_DATA_LSB
00166 #else
00167     {
00168         unsigned int val = 0;
00169 
00170         if (rc & LCD_D0) {
00171             val |= 0x01;
00172         }
00173         if (rc & LCD_D1) {
00174             val |= 0x02;
00175         }
00176         if (rc & LCD_D2) {
00177             val |= 0x04;
00178         }
00179         if (rc & LCD_D3) {
00180             val |= 0x08;
00181         }
00182         rc = val;
00183     }
00184 #endif
00185     return rc;
00186 }
00187 
00191 static unsigned int LcdReadByte(void)
00192 {
00193     outr(LCD_DATA_PIO_ID+PIO_ODR_OFF, LCD_DATA);
00194     LcdDelay(LCD_SHORT_DELAY);
00195     LCD_RW_SET();
00196     LcdDelay(LCD_SHORT_DELAY);
00197     return (LcdReadNibble() << 4) | LcdReadNibble();
00198 }
00199 
00203 static unsigned int LcdReadStatus(void)
00204 {
00205     /* RS low selects status register. */
00206     LCD_RS_CLR();
00207     return LcdReadByte();
00208 }
00209 
00210 #endif                          /* HD44_RW_BIT */
00211 
00212 #if 0
00213 /* This function is a bit critical as some chipsets are known to
00214  * release the redy bit some time early. So after rady goes low,
00215  * another fixed delay has to be added before soing the next access.
00216  */
00217 static void LcdWaitReady(unsigned int delay)
00218 {
00219     while (delay--) {
00220 #if defined(HD44_RW_BIT)
00221         if ((LcdReadStatus() & _BV(LCD_BUSY)) == 0) {
00222             break;
00223         }
00224 #endif
00225         NutMicroDelay(1);
00226     }
00227 }
00228 #endif
00229 
00235 static void LcdWriteNibble(unsigned int nib)
00236 {
00237 #ifdef LCD_DATA_LSB
00238     nib <<= LCD_DATA_LSB;
00239 #else
00240     {
00241         unsigned int val = 0;
00242         if (nib & 0x01) {
00243             val |= LCD_D0;
00244         }
00245         if (nib & 0x02) {
00246             val |= LCD_D1;
00247         }
00248         if (nib & 0x04) {
00249             val |= LCD_D2;
00250         }
00251         if (nib & 0x08) {
00252             val |= LCD_D3;
00253         }
00254         nib = val;
00255     }
00256 #endif
00257     LcdSetBits(nib & LCD_DATA);
00258     LcdClrBits(~nib & LCD_DATA);
00259 
00260     /* Create Enable Pulse:
00261      * For HD44780 Displays we need:
00262      * Vcc = 5.0V -> PWeh >= 230ns
00263      * Vcc = 3.3V -> PWeh >= 500ns
00264      */
00265     LCD_EN_SET();
00266     LcdNanoDelay(LCD_PW_EH);
00267     LCD_EN_CLR();
00268 }
00269 
00275 static void LcdWriteByte(unsigned int data)
00276 {
00277     /* If configured set RW low */
00278 #ifdef LCD_RW_BIT
00279     LCD_RW_CLR();
00280 #endif
00281 
00282     /* If using 4-bit access, write two nibbles now */
00283 #ifdef LCD_IF_4BIT
00284     LcdWriteNibble(data >> 4);
00285     LcdNanoDelay(LCD_PW_EH);
00286     LcdWriteNibble(data);
00287 #else
00288     /* else write one byte */
00289     data <<= LCD_DATA_LSB;
00290     LcdSetBits(data & LCD_DATA);
00291     LcdClrBits(~data & LCD_DATA);
00292 #endif
00293 
00294     /* If configured, let the task sleep before next character */
00295 #if defined(LCD_SLEEP_DLY)
00296     NutSleep(1);
00297 #else
00298     /* or add a fixed delay and immediately process next char */
00299     NutMicroDelay(LCD_E2E_DLY);
00300 #endif
00301 }
00302 
00308 static void LcdWriteCmd(uint8_t cmd)
00309 {
00310     /* RS low selects instruction register. */
00311     LCD_RS_CLR();
00312     LcdWriteByte(cmd);
00313 }
00314 
00315 static void LcdWriteInstruction(uint8_t cmd, uint8_t xt)
00316 {
00317     LcdWriteCmd(cmd);
00318 }
00319 
00325 static void LcdWriteData(uint8_t data)
00326 {
00327     /* RS high selects data register. */
00328     LCD_RS_SET();
00329     LcdWriteByte(data);
00330 }
00331 
00332 static void LcdSetCursor(uint8_t pos)
00333 {
00334     uint8_t offset[] = {
00335 #ifdef LCD_KS0073
00336         0x00, 0x20, 0x40, 0x60
00337 #elif (LCD_COLS >= 20)
00338         0x00, 0x40, 0x14, 0x54
00339 #else
00340         0x00, 0x40, 0x10, 0x50
00341 #endif
00342     };
00343 
00344     pos = offset[(pos / LCD_COLS) % LCD_ROWS] + pos % LCD_COLS;
00345     LcdWriteCmd(1 << LCD_DDRAM | pos);
00346 }
00347 
00348 static void LcdCursorHome(void)
00349 {
00350     LcdWriteCmd(1 << LCD_HOME);
00351     NutSleep(2);
00352 }
00353 
00354 static void LcdCursorLeft(void)
00355 {
00356     LcdWriteCmd(1 << LCD_MOVE);
00357 }
00358 
00359 static void LcdCursorRight(void)
00360 {
00361     LcdWriteCmd(1 << LCD_MOVE | 1 << LCD_MOVE_RIGHT);
00362 }
00363 
00364 static void LcdClear(void)
00365 {
00366     LcdWriteCmd(_BV(LCD_CLR));
00367     NutSleep(2);
00368 }
00369 
00370 static void LcdCursorMode(uint8_t on)
00371 {
00372     LcdWriteCmd(1 << LCD_ON_CTRL | on ? 1 << LCD_ON_CURSOR : 0x00);
00373 }
00374 
00375 static int LcdInit(NUTDEVICE * dev)
00376 {
00377 #if defined(PMC_PCER)
00378     outr(PMC_PCER, _BV(LCD_RS_PIO_ID) | _BV(LCD_EN_PIO_ID));
00379 #endif
00380 
00381     /* Initialize GPIO lines. */
00382 #ifdef LCD_RW_BIT
00383     outr(PMC_PCER, _BV(LCD_RW_PIO_ID));
00384     LCD_RW_CLR();
00385 #endif
00386 
00387 #ifdef LCD_EN2_BIT
00388     outr(PMC_PCER, _BV(LCD_EN2_PIO_ID));
00389     LCD_EN2_CLR();
00390 #endif
00391 
00392 #ifdef LCD_RST_BIT
00393     outr(PMC_PCER, _BV(LCD_RST_PIO_ID));
00394     LCD_RST_CLR();
00395 #endif
00396 
00397     LCD_RS_CLR();
00398     LCD_RW_CLR();
00399     LcdClrBits(LCD_DATA);
00400     NutMicroDelay(30);
00401     LCD_EN_CLR();
00402     NutMicroDelay(30);
00403 
00404     /* Initial delay. Actually only required after power on. */
00405     NutSleep(16);
00406 
00407     /* This initialization will make sure, that the LCD is switched
00408      * to 8-bit mode, no matter which mode we start from or we finally
00409      * need.
00410      */
00411     LcdWriteNibble((_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_8BIT)) >> 4);
00412     NutSleep(15);
00413     LcdWriteNibble((_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_8BIT)) >> 4);
00414     NutSleep(4);
00415     LcdWriteNibble((_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_8BIT)) >> 4);
00416     NutSleep(2);
00417 
00418 #ifdef LCD_IF_4BIT
00419     /* We now switch to 4-bit mode */
00420     LcdWriteNibble(_BV(LCD_FUNCTION) >> 4);
00421     NutSleep(2);
00422 
00423     // TODO: Add support for large font in single line displays
00424     /* Set number of lines and font. Can't be changed later. */
00425 #if (LCD_ROWS == 2) || (LCD_ROWS==4)
00426     LcdWriteNibble((_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_2LINES)) >> 4);
00427     LcdWriteNibble(_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_2LINES));
00428 #else
00429     LcdWriteNibble(_BV(LCD_FUNCTION) >> 4);
00430     LcdWriteNibble(_BV(LCD_FUNCTION) );
00431 #endif
00432 #else /* LCD_IF_4BIT */
00433     LcdWriteCmd(_BV(LCD_FUNCTION) | _BV(LCD_FUNCTION_8BIT));
00434 #endif /* LCD_IF_4BIT */
00435     NutSleep(2);
00436 
00437     /* Switch display and cursor off. */
00438     LcdWriteNibble(_BV(LCD_ON_CTRL) >> 4);
00439     LcdWriteNibble(_BV(LCD_ON_CTRL));
00440     NutSleep(2);
00441 
00442     /* Clear display. */
00443     LcdClear();
00444 
00445     /* Set entry mode. */
00446     LcdWriteCmd(_BV(LCD_ENTRY_MODE) | _BV(LCD_ENTRY_INC));
00447     /* Switch display on. */
00448     LcdWriteCmd(_BV(LCD_ON_CTRL) | _BV(LCD_ON_DISPLAY));
00449     /* Move cursor home. */
00450     LcdCursorHome();
00451     /* Set data address to zero. */
00452     LcdWriteCmd(_BV(LCD_DDRAM));
00453 
00454     return 0;
00455 }
00456 
00460 TERMDCB dcb_term = {
00461     LcdInit,                    
00462     LcdWriteData,               
00463     LcdWriteInstruction,        
00464     LcdClear,                   
00465     LcdSetCursor,               
00466     LcdCursorHome,              
00467     LcdCursorLeft,              
00468     LcdCursorRight,             
00469     LcdCursorMode,              
00470     0,                          
00471     0,                          
00472     LCD_ROWS,                   
00473     LCD_COLS,                   
00474     LCD_COLS,                   
00475     0,                          
00476     0,                          
00477     0                           
00478 };
00479 
00483 NUTDEVICE devLcd = {
00484     0,                          
00485     {'c', 'h', 'a', 'r', 'l', 'c', 'd', 0, 0},  
00486     IFTYP_STREAM,               
00487     0,                          
00488     0,                          
00489     0,                          
00490     &dcb_term,                  
00491     TermInit,                   
00492     TermIOCtl,                  
00493     0,
00494     TermWrite,
00495     TermOpen,
00496     TermClose,
00497     0
00498 };
00499