/**************************************************************************** * configs/pcblocic-pic32mx/src/pic32mx_lcd1602.c * * This logic supports the connection of an LCD1602 LCD to the PCB Logic * PIC32MX board. The LCD1602 is based on the Hitachi HD44780U LCD * controller * * Copyright (C) 2013 Gregory Nutt. All rights reserved. * Authors: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /* LCD pin mapping (see configs/pcblogic-pic32mx/README.txt) * * ----------------------------------- ---------- ---------------------------------- * PIC32 LCD1602 PCBLogic PIN * PIN SIGNAL NAME PIN NAME(s) * ----------------------------------- ---------- ---------------------------------- * 1. Vss --> Powerpoint GND * 2. Vdd --> Powerpoint USB+5V * 3. Vee N/C To ground via 10K potentiometer * 44 AN15/OCFB/PMALL/PMA0/CN12/RB15 4. RS 4 PMA0, Selects registers * 82 PMRD/CN14/RD5 5. RW 82 PMRD/PMWR, Selects read or write * 81 OC5/PMWR/CN13/RD4 6. E 81 PMENB, Starts data read/write * 93 PMD0/RE0 7. D0 93 PMD0 * 94 PMD1/RE1 8. D1 94 PMD1 * 98 PMD2/RE2 9. D2 98 PMD2 * 99 PMD3/RE3 10. D3 99 PMD3 * 100 PMD4/RE4 11. D4 100 PMD4 * 3 PMD5/RE5 12. D5 3 PMD5 * 4 PMD6/RE6 13. D6 4 PMD6 * 5 PMD7/RE7 14. D7 5 PMD7 * 15. A N/C To Vcc (5V) via 10K potentiometer * 16. K --> Powerpoint GND * ----------------------------------- ---------- ---------------------------------- */ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "up_arch.h" #include "pic32mx-pmp.h" #include "pic32mx-int.h" #include "pic32mx-internal.h" #include "pcblogic-pic32mx.h" #ifdef CONFIG_LCD_LCD1602 /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Configuration ************************************************************/ #ifndef CONFIG_PIC32MX_PMP # error "CONFIG_PIC32MX_PMP is required to use the LCD" #endif /* Define CONFIG_DEBUG_LCD to enable detailed LCD debug output. Verbose debug must * also be enabled. */ #ifndef CONFIG_DEBUG # undef CONFIG_DEBUG_VERBOSE # undef CONFIG_DEBUG_GRAPHICS # undef CONFIG_DEBUG_LCD #endif #ifndef CONFIG_DEBUG_VERBOSE # undef CONFIG_DEBUG_LCD #endif /* The ever-present MIN/MAX macros ******************************************/ #ifndef MIN # define MIN(a,b) (a < b ? a : b) #endif #ifndef MAX # define MAX(a,b) (a > b ? a : b) #endif /* Pin configuration ********************************************************/ /* RB15, RS -- High values selects data */ #define GPIO_LCD_RS (GPIO_OUTPUT|GPIO_VALUE_ZERO|GPIO_PORTB|GPIO_PIN15) /* LCD **********************************************************************/ #define LCD_NROWS 2 #define LCD_NCOLUMNS 16 #define LCD_NCHARS (LCD_NROWS * LCD_NCOLUMNS) /* Debug ********************************************************************/ #ifdef CONFIG_DEBUG_LCD # define lcddbg dbg # define lcdvdbg vdbg #else # define lcddbg(x...) # define lcdvdbg(x...) #endif /**************************************************************************** * Private Type Definition ****************************************************************************/ /* SLCD incoming stream structure */ struct lcd_instream_s { struct lib_instream_s stream; FAR const char *buffer; ssize_t nbytes; }; /* Global LCD state */ struct lcd1602_2 { bool initialized; /* True: Completed initialization sequence */ uint8_t currow; /* Current row */ uint8_t curcol; /* Current column */ }; /**************************************************************************** * Private Function Protototypes ****************************************************************************/ /* Debug */ #if defined(CONFIG_DEBUG_LCD) && defined(CONFIG_DEBUG_VERBOSE) static void lcd_dumpstate(FAR const char *msg); static void lcd_dumpstream(FAR const char *msg, FAR const struct lcd_instream_s *stream); #else # define lcd_dumpstate(msg) # define lcd_dumpstream(msg, stream) #endif /* Internal functions */ static int lcd_getstream(FAR struct lib_instream_s *instream); static void lcd_wrcommand(uint8_t cmd); static void lcd_wrdata(uint8_t data); static uint8_t lcd_rddata(void); static uint8_t lcd_readch(uint8_t row, uint8_t column); static void lcd_writech(uint8_t ch, uint8_t row, uint8_t column); static void lcd_appendch(uint8_t ch); static void lcd_action(enum slcdcode_e code, uint8_t count); /* Character driver operations */ static ssize_t lcd_read(FAR struct file *, FAR char *, size_t); static ssize_t lcd_write(FAR struct file *, FAR const char *, size_t); static int lcd_ioctl(FAR struct file *filep, int cmd, unsigned long arg); #ifndef CONFIG_DISABLE_POLL static int lcd_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); #endif /**************************************************************************** * Private Data ****************************************************************************/ /* Character driver operations */ static const struct file_operations g_lcdops = { 0, /* open */ 0, /* close */ lcd_read, /* read */ lcd_write, /* write */ 0, /* seek */ lcd_ioctl /* ioctl */ #ifndef CONFIG_DISABLE_POLL , lcd_poll /* poll */ #endif }; /* This is the driver state structure */ static struct lcd1602_2 g_lcd1602; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: lcd_dumpstate ****************************************************************************/ #if defined(CONFIG_DEBUG_LCD) && defined(CONFIG_DEBUG_VERBOSE) static void lcd_dumpstate(FAR const char *msg) { uint8_t buffer[LCD_NCOLUMNS]; uint8_t ch; int row; int column; lcdvdbg("%s:\n", msg); lcdvdbg(" currow: %d curcol: %d\n", g_lcd1602.currow, g_lcd1602.curcol); for (row = 0, column = 0; row < LCD_NROWS; ) { ch = lcd_readch(row, column); buffer[column] = isprint(ch) ? ch : '.'; if (++column >= LCD_NCOLUMNS) { lcdvdbg(" [%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c]\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8], buffer[9], buffer[10], buffer[11], buffer[12], buffer[13], buffer[14], buffer[15]); column = 0; row++; } } } #endif /**************************************************************************** * Name: lcd_dumpstate ****************************************************************************/ #if defined(CONFIG_DEBUG_LCD) && defined(CONFIG_DEBUG_VERBOSE) static void lcd_dumpstream(FAR const char *msg, FAR const struct lcd_instream_s *stream) { lcdvdbg("%s:\n", msg); lcdvdbg(" nget: %d nbytes: %d\n", stream->stream.nget, stream->nbytes); lib_dumpbuffer("STREAM", stream->buffer, stream->nbytes); } #endif /**************************************************************************** * Name: lcd_getstream * * Description: * Get one character from the keyboard. * ****************************************************************************/ static int lcd_getstream(FAR struct lib_instream_s *instream) { FAR struct lcd_instream_s *lcdstream = (FAR struct lcd_instream_s *)instream; DEBUGASSERT(lcdstream && lcdstream->buffer); if (lcdstream->nbytes > 0) { lcdstream->nbytes--; lcdstream->stream.nget++; return (int)*lcdstream->buffer++; } return EOF; } /**************************************************************************** * Name: lcd_wrcommand * * Description: * Configure to write an LCD command * ****************************************************************************/ static void lcd_wrcommand(uint8_t cmd) { /* Address bit A0 is RS. Set the address latch to A0=0 */ putreg32(1, PIC32MX_PMP_ADDRCLR); /* And write the command to the data out register */ putreg32((uint32_t)cmd, PIC32MX_PMP_DOUT); } /**************************************************************************** * Name: lcd_wrdata * * Description: * Configure to read or write LCD data * ****************************************************************************/ static void lcd_wrdata(uint8_t data) { /* Address bit A0 is RS. Set the address latch to A0=1 */ putreg32(1, PIC32MX_PMP_ADDRSET); /* And write the data to the data out register */ putreg32((uint32_t)data, PIC32MX_PMP_DOUT); } /**************************************************************************** * Name: lcd_rddata * * Description: * Configure to read or write LCD data * ****************************************************************************/ static uint8_t lcd_rddata(void) { /* Address bit A0 is RS. Set the address latch to A0=1 */ putreg32(1, PIC32MX_PMP_ADDRSET); /* And read the data to the data in register */ return (uint8_t)getreg32(PIC32MX_PMP_DIN); } /**************************************************************************** * Name: lcd_readch ****************************************************************************/ static uint8_t lcd_readch(uint8_t row, uint8_t column) { uint8_t addr; /* Set the cursor position. Internally, the HD44780U supports a display * size of up to 2x40 addressed as follows: * * Column 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... 39 * Row 0 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ... 27 * Ro1 1 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f ... 67 */ addr = column; if (row > 0) { addr |= HD4478OU_DDRAM_ROW1; } lcd_wrcommand(HD4478OU_DDRAM_AD(addr)); /* And write the character here */ return lcd_rddata(); } /**************************************************************************** * Name: lcd_writech ****************************************************************************/ static void lcd_writech(uint8_t ch, uint8_t row, uint8_t column) { uint8_t addr; /* Set the cursor position. Internally, the HD44780U supports a display * size of up to 2x40 addressed as follows: * * Column 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... 39 * Row 0 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ... 27 * Ro1 1 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f ... 67 */ addr = column; if (row > 0) { addr |= HD4478OU_DDRAM_ROW1; } lcd_wrcommand(HD4478OU_DDRAM_AD(addr)); /* And write the character here */ lcd_wrdata(ch); } /**************************************************************************** * Name: lcd_appendch ****************************************************************************/ static void lcd_appendch(uint8_t ch) { if (g_lcd1602.curcol < LCD_NCOLUMNS) { lcd_writech(ch, g_lcd1602.currow, g_lcd1602.curcol); g_lcd1602.curcol++; } } /**************************************************************************** * Name: lcd_action ****************************************************************************/ static void lcd_action(enum slcdcode_e code, uint8_t count) { lcdvdbg("Action: %d count: %d\n", code, count); lcd_dumpstate("BEFORE ACTION"); switch (code) { /* Erasure */ case SLCDCODE_BACKDEL: /* Backspace (backward delete) N characters */ { int tmp; /* If we are at the home position or if the count is zero, then ignore the action */ if (g_lcd1602.curcol < 1 || count < 1) { break; } /* Otherwise, BACKDEL is like moving the cursor back N characters then doing a * forward deletion. Decrement the cursor position and fall through. */ tmp = (int)g_lcd1602.curcol - count; if (tmp < 0) { tmp = 0; count = g_lcd1602.curcol; } /* Save the updated cursor positions */ g_lcd1602.curcol = tmp; } case SLCDCODE_FWDDEL: /* DELete (forward delete) N characters moving text */ if (count > 0) { int nchars; int nmove; int i; /* How many characters are to the right of the cursor position * (including the one at the cursor position)? Then get the * number of characters to move. */ nchars = LCD_NCOLUMNS - g_lcd1602.curcol; nmove = MIN(nchars, count) - 1; /* Move all characters after the current cursor position left by 'nmove' characters */ for (i = g_lcd1602.curcol + nmove; i < LCD_NCOLUMNS - 1; i++) { uint8_t ch = lcd_readch(g_lcd1602.currow, i); lcd_writech(ch, g_lcd1602.currow, i - nmove); } /* Erase the last 'nmove' characters on the display */ for (i = LCD_NCOLUMNS - nmove; i < LCD_NCOLUMNS; i++) { lcd_writech(' ', i, 0); } } break; case SLCDCODE_ERASE: /* Erase N characters from the cursor position */ if (count > 0) { int last; int i; /* Get the last position to clear and make sure that the last * position is on the SLCD. */ last = g_lcd1602.curcol + count - 1; if (last >= LCD_NCOLUMNS) { last = LCD_NCOLUMNS - 1; } /* Erase N characters after the current cursor position left by one */ for (i = g_lcd1602.curcol; i < last; i++) { lcd_writech(' ', g_lcd1602.currow, i); } } break; case SLCDCODE_CLEAR: /* Home the cursor and erase the entire display */ { /* Clear the display */ lcd_wrcommand(HD4478OU_CLEAR); /* And home the cursor */ g_lcd1602.currow = 0; g_lcd1602.curcol = 0; } break; case SLCDCODE_ERASEEOL: /* Erase from the cursor position to the end of line */ { int i; /* Erase characters after the current cursor position to the end of the line */ for (i = g_lcd1602.curcol; i < LCD_NCOLUMNS; i++) { lcd_writech(' ', g_lcd1602.currow, i); } } break; /* Cursor movement */ case SLCDCODE_HOME: /* Cursor home */ { g_lcd1602.currow = 0; g_lcd1602.curcol = 0; } break; case SLCDCODE_END: /* Cursor end */ { g_lcd1602.curcol = LCD_NCOLUMNS - 1; } break; case SLCDCODE_LEFT: /* Cursor left by N characters */ { int tmp = (int)g_lcd1602.curcol - count; /* Don't permit movement past the beginning of the SLCD */ if (tmp < 0) { tmp = 0; } /* Save the new cursor position */ g_lcd1602.curcol = (uint8_t)tmp; } break; case SLCDCODE_RIGHT: /* Cursor right by N characters */ { int tmp = (int)g_lcd1602.curcol + count; /* Don't permit movement past the end of the SLCD */ if (tmp >= LCD_NCOLUMNS) { tmp = LCD_NCOLUMNS - 1; } /* Save the new cursor position */ g_lcd1602.curcol = (uint8_t)tmp; } break; case SLCDCODE_UP: /* Cursor up by N lines */ { int tmp = (int)g_lcd1602.currow - count; /* Don't permit movement past the top of the SLCD */ if (tmp < 0) { tmp = 0; } /* Save the new cursor position */ g_lcd1602.currow = (uint8_t)tmp; } break; case SLCDCODE_DOWN: /* Cursor down by N lines */ { int tmp = (int)g_lcd1602.currow + count; /* Don't permit movement past the bottom of the SLCD */ if (tmp >= LCD_NROWS) { tmp = LCD_NROWS - 1; } /* Save the new cursor position */ g_lcd1602.currow = (uint8_t)tmp; } break; case SLCDCODE_PAGEUP: /* Cursor up by N pages */ case SLCDCODE_PAGEDOWN: /* Cursor down by N pages */ break; /* Not supportable on this SLCD */ /* Blinking */ case SLCDCODE_BLINKSTART: /* Start blinking with current cursor position */ case SLCDCODE_BLINKEND: /* End blinking after the current cursor position */ case SLCDCODE_BLINKOFF: /* Turn blinking off */ break; /* Not implemented */ /* These are actually unreportable errors */ default: case SLCDCODE_NORMAL: /* Not a special keycode */ break; } lcd_dumpstate("AFTER ACTION"); } /**************************************************************************** * Name: lcd_read ****************************************************************************/ static ssize_t lcd_read(FAR struct file *filep, FAR char *buffer, size_t len) { uint8_t row; uint8_t column; int nread; /* Try to read the entire display. Notice that the seek offset * (filep->f_pos) is ignored. It probably should be taken into account * and also updated after each read and write. */ row = 0; column = 0; for (nread = 0; nread < len; nread++) { *buffer++ = lcd_readch(row, column); if (++column >= LCD_NCOLUMNS) { column = 0; if (++row >= LCD_NROWS) { break; } } } return nread; } /**************************************************************************** * Name: lcd_write ****************************************************************************/ static ssize_t lcd_write(FAR struct file *filep, FAR const char *buffer, size_t len) { struct lcd_instream_s instream; struct slcdstate_s state; enum slcdret_e result; uint8_t ch; uint8_t count; /* Initialize the stream for use with the SLCD CODEC */ instream.stream.get = lcd_getstream; instream.stream.nget = 0; instream.buffer = buffer; instream.nbytes = len; lcd_dumpstream("BEFORE WRITE", &instream); /* Now decode and process every byte in the input buffer */ memset(&state, 0, sizeof(struct slcdstate_s)); while ((result = slcd_decode(&instream.stream, &state, &ch, &count)) != SLCDRET_EOF) { lcdvdbg("slcd_decode returned result=%d char=%d count=%d\n", result, ch, count); if (result == SLCDRET_CHAR) /* A normal character was returned */ { /* Check for ASCII control characters */ if (ch < ASCII_SPACE) { /* All are ignored except for backspace and carriage return */ if (ch == ASCII_BS) { /* Perform the backward deletion */ lcd_action(SLCDCODE_BACKDEL, 1); } else if (ch == ASCII_CR) { /* Perform the carriage return */ g_lcd1602.curcol = 0; lcd_action(SLCDCODE_DOWN, 1); } } /* Handle ASCII_DEL */ else if (ch == ASCII_DEL) { /* Perform the forward deletion */ lcd_action(SLCDCODE_FWDDEL, 1); } /* The rest of the 7-bit ASCII characters are fair game */ else if (ch < 128) { /* Write the character if it valid */ lcd_appendch(ch); } } else /* (result == SLCDRET_SPEC) */ /* A special SLCD action was returned */ { /* Then Perform the action */ lcd_action((enum slcdcode_e)ch, count); } } /* Assume that the entire input buffer was processed */ lcd_dumpstream("AFTER WRITE", &instream); return (ssize_t)len; } /**************************************************************************** * Name: lcd_ioctl ****************************************************************************/ static int lcd_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { switch (cmd) { /* SLCDIOC_GETATTRIBUTES: Get the attributes of the SLCD * * argument: Pointer to struct slcd_attributes_s in which values will be * returned */ case SLCDIOC_GETATTRIBUTES: { FAR struct slcd_attributes_s *attr = (FAR struct slcd_attributes_s *)((uintptr_t)arg); lcdvdbg("SLCDIOC_GETATTRIBUTES:\n"); if (!attr) { return -EINVAL; } attr->nrows = LCD_NROWS; attr->ncolumns = LCD_NCOLUMNS; attr->nbars = 0; attr->maxcontrast = 0; attr->maxbrightness = 0 } break; /* SLCDIOC_CURPOS: Get the SLCD cursor positioni (rows x characters) * * argument: Pointer to struct slcd_curpos_s in which values will be * returned */ case SLCDIOC_CURPOS: { FAR struct slcd_curpos_s *curpos = (FAR struct slcd_curpos_s *)((uintptr_t)arg); lcdvdbg("SLCDIOC_CURPOS: row=%d column=%d\n", g_lcd1602.currow, g_lcd1602.curcol); if (!curpos) { return -EINVAL; } curpos->row = g_lcd1602.currow; curpos->column = g_lcd1602.curcol; } break; case SLCDIOC_SETBAR: /* SLCDIOC_SETBAR: Set bars on a bar display */ case SLCDIOC_GETCONTRAST: /* SLCDIOC_GETCONTRAST: Get the current contrast setting */ case SLCDIOC_SETCONTRAST: /* SLCDIOC_SETCONTRAST: Set the contrast to a new value */ case SLCDIOC_GETBRIGHTNESS: /* Get the current brightness setting */ case SLCDIOC_SETBRIGHTNESS: /* Set the brightness to a new value */ default: return -ENOTTY; } return OK; } /**************************************************************************** * Name: lcd_poll ****************************************************************************/ #ifndef CONFIG_DISABLE_POLL static int lcd_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) { if (setup) { /* Data is always avaialble to be read */ fds->revents |= (fds->events & (POLLIN|POLLOUT)); if (fds->revents != 0) { sem_post(fds->sem); } } return OK; } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: up_lcd1602_initialize * * Description: * Initialize the LCD1602 hardware and register the character driver as * /dev/lcd1602. Prototype is in include/nuttx/lcd/hd4478ou.h. * ****************************************************************************/ int up_lcd1602_initialize(void) { uint32_t regval; int ret = OK; /* Only initialize the driver once. */ if (!g_lcd1602.initialized) { lcdvdbg("Initializing\n"); /* PMP Master mode configuration */ /* Make sure that interrupts are disabled */ putreg32(INT_PMP, PIC32MX_INT_IEC1CLR); /* Stop and reset the PMP module and clear the mode and control registers. */ putreg32(0, PIC32MX_PMP_MODE); putreg32(0, PIC32MX_PMP_AEN); putreg32(0, PIC32MX_PMP_CON); putreg32(0, PIC32MX_PMP_ADDR); /* Set LCD timing values, PMP master mode 3, 8-bit mode, no address * increment, and no interrupts. */ regval = (PMP_MODE_WAITE_RD(0) | PMP_MODE_WAITM(3) | PMP_MODE_WAITB_1TPB | PMP_MODE_MODE_MODE1 | PMP_MODE_MODE8 | PMP_MODE_INCM_NONE | PMP_MODE_IRQM_NONE); putreg32(regval, PIC32MX_PMP_MODE); /* Enable the PMP for reading and writing * PMRD/PMWR is active high (1=RD; 0=WR) * PMENB is active high. * No chip selects * Address latch is active high * Enable PMRD/PMWR, PMENB, and the PMP. */ regval = (PMP_CON_RDSP | PMP_CON_WRSP | PMP_CON_ALP | PMP_CON_CSF_ADDR1415 | PMP_CON_PTRDEN | PMP_CON_PTWREN | PMP_CON_ADRMUX_NONE | PMP_CON_ON); putreg32(regval, PIC32MX_PMP_CON); /* Configure and enable the LCD */ /* Wait > 15 milliseconds afer Vdd > 4.5V */ up_mdelay(100); /* Select the 8-bit interface. BF cannot be checked before this command. * This needs to be done a few times with some magic delays. */ lcd_wrcommand(HD4478OU_FUNC | HD4478OU_FUNC_DL8D | HD4478OU_FUNC_N1); up_mdelay(50); lcd_wrcommand(HD4478OU_FUNC | HD4478OU_FUNC_DL8D | HD4478OU_FUNC_N1); up_udelay(50); lcd_wrcommand(HD4478OU_FUNC | HD4478OU_FUNC_DL8D | HD4478OU_FUNC_N1); lcd_wrcommand(HD4478OU_FUNC | HD4478OU_FUNC_DL8D | HD4478OU_FUNC_N1); /* Configure the display */ lcd_wrcommand(HD4478OU_DISPLAY); /* Display, cursor, and blink off */ lcd_wrcommand(HD4478OU_CLEAR); /* Clear the display */ lcd_wrcommand(HD4478OU_INPUT | HD4478OU_INPUT_INCR); /* Increment mode */ lcd_wrcommand(HD4478OU_DISPLAY | HD4478OU_DISPLAY_ON); /* Display on, cursor and blink off */ lcd_wrcommand(HD4478OU_DDRAM_AD(0)); /* Select DDRAM RAM AD=0 */ /* Register the LCD device driver */ ret = register_driver("/dev/lcd1602", &g_lcdops, 0644, &g_lcd1602); g_lcd1602.initialized = true; } return ret; } #endif /* CONFIG_LCD_LCD1602 */