/**************************************************************************** * configs/sure-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/sure-pic32mx/README.txt) * * --------------------- ---------- ---------------------------------- * PIC32 Sure JP1 Sure Signal Description * PIN SIGNAL NAME PIN NAME(s) * --------------------- ---------- ---------------------------------- * 34 Vbus 1. +5V +5V VBUS device mode * To GND via capacitor * 2. GND GND * 49 RD1 3. Vo Transistor circuit driven by PWM2 * 44 PMA0/AN15/RB15 4. RS PMA0, Selects registers * 53 PMRD/RD5 5. RW PMRD/PMWR, Selects read or write * 45 PMPCS1/RD11 6. E Starts data read/write * 60 PMD0/RE0 7. DB0 PMD0 * 61 PMD1/RE1 8. DB1 PMD1 * 62 PMD2/RE2 9. DB2 PMD2 * 63 PMD3/RE3 10. DB3 PMD3 * 64 PMD4/RE4 11. DB4 PMD4 * 1 PMD5/RE5 12. DB5 PMD5 * 2 PMD6/RE6 13. DB6 PMD6 * 3 PMD7/RE7 14. DB7 PMD7 * 15. A +5V_DUSB * 46 INT0/RD0 16. K Transistor circuit driven by PWM1 * --------------------- ---------- ---------------------------------- * * Vbus power also requires Vbuson/AN5/RB5 */ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "up_arch.h" #include "pic32mx-ioport.h" #include "pic32mx-int.h" #include "pic32mx-internal.h" #include "sure-pic32mx.h" #ifdef CONFIG_LCD_LCD1602 /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Configuration ************************************************************/ #ifndef CONFIG_LCD_MAXCONTRAST # define CONFIG_LCD_MAXCONTRAST 100 #endif #ifndef CONFIG_LCD_MAXPOWER # define CONFIG_LCD_MAXPOWER 100 #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 /* LCD **********************************************************************/ #define LCD_NROWS 2 #define LCD_NCOLUMNS 16 #define LCD_NCHARS (LCD_NROWS * LCD_NCOLUMNS) #define NOP __asm__ __volatile__ ("nop"); /* 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 */ uint8_t brightness; /* Current brightness */ }; /**************************************************************************** * 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_brightness(uint8_t brightness); static void lcd_shortdelay(int delay); static void lcd_wrcommand(uint8_t cmd); static void lcd_wrdata(uint8_t data); static uint8_t lcd_rddata(void); static uint8_t lcd_readstatus(void); static void lcd_waitbusy(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 *filp, int cmd, unsigned long arg); #ifndef CONFIG_DISABLE_POLL static int lcd_poll(FAR struct file *filp, 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_brightness * * Description: * Enable for disable LCD lighting. * ****************************************************************************/ static void lcd_brightness(uint8_t brightness) { /* The LIGHT and COMP pins are label PWM1 and PWM2 and so are obviously * intended to support modulated outputs. However, here for simplicity, * they are just treated as on/off discretes outputs. */ if (brightness > 0) { /* Turn the LCD light on */ pic32mx_gpiowrite(GPIO_LCD_LIGHT, true); NOP;NOP;NOP; pic32mx_gpiowrite(GPIO_LCD_COMP, true); NOP;NOP; pic32mx_gpiowrite(GPIO_LCD_PWR, true); } else { /* Turn the LCD light off */ pic32mx_gpiowrite(GPIO_LCD_PWR, false); pic32mx_gpiowrite(GPIO_LCD_COMP, false); pic32mx_gpiowrite(GPIO_LCD_LIGHT, false); } g_lcd1602.brightness = brightness; } /**************************************************************************** * Name: lcd_shortdelay * * Description: * Small delays are needed to make some of the LCD operations work. * ****************************************************************************/ static void lcd_shortdelay(int delay) { volatile int loop; /* On a 32MHz MCU, this should amount to about 300NS per loop */ while (delay-- > 0) { for (loop = 0; loop < 1; loop++) { NOP; } } } /**************************************************************************** * Name: lcd_wrcommand * * Description: * Configure to write an LCD command * ****************************************************************************/ static void lcd_wrcommand(uint8_t cmd) { /* Make sure that the LCD is available */ lcd_waitbusy(); /* Select DB0-15 as outputs (only DB-0-7 are actually used) */ putreg16(0, PIC32MX_IOPORTE_TRIS); /* Set up to write the commond */ pic32mx_gpiowrite(GPIO_LCD_RS, false); /* Select command */ pic32mx_gpiowrite(GPIO_LCD_RW, false); /* Select write */ lcd_shortdelay(2); pic32mx_gpiowrite(GPIO_LCD_E, true); /* Enable transfer */ lcd_shortdelay(1); /* Write the command to the LCD */ putreg16(cmd, PIC32MX_IOPORTE_PORT); lcd_shortdelay(1); pic32mx_gpiowrite(GPIO_LCD_E, false); } /**************************************************************************** * Name: lcd_wrdata * * Description: * Configure to read or write LCD data * ****************************************************************************/ static void lcd_wrdata(uint8_t data) { /* Make sure that the LCD is available */ lcd_waitbusy(); /* Select DB0-15 as outputs (only DB-0-7 are actually used) */ putreg16(0, PIC32MX_IOPORTE_TRIS); /* Set up to write the data */ pic32mx_gpiowrite(GPIO_LCD_RS, true); /* Select data */ pic32mx_gpiowrite(GPIO_LCD_RW, false); /* Select write */ lcd_shortdelay(2); pic32mx_gpiowrite(GPIO_LCD_E, true); /* Enable transfer */ lcd_shortdelay(1); /* Write the data to the LCD */ putreg16(data, PIC32MX_IOPORTE_PORT); /* Write the data */ lcd_shortdelay(1); pic32mx_gpiowrite(GPIO_LCD_E, false); } /**************************************************************************** * Name: lcd_rddata * * Description: * Configure to read or write LCD data * ****************************************************************************/ static uint8_t lcd_rddata(void) { /* Make sure that the LCD is available */ lcd_waitbusy(); /* Setup to read data */ pic32mx_gpiowrite(GPIO_LCD_RS, true); /* Select data */ pic32mx_gpiowrite(GPIO_LCD_RW, true); /* Select read */ lcd_shortdelay(2); pic32mx_gpiowrite(GPIO_LCD_E, true); /* Enable transfer */ lcd_shortdelay(1); putreg16(0xff, PIC32MX_IOPORTE_TRISSET); /* Set DB0-7 as inputs */ pic32mx_gpiowrite(GPIO_LCD_E, false); /* Disable transfer */ /* Read the data from the LCD */ return (uint8_t)getreg16(PIC32MX_IOPORTE_PORT); } /**************************************************************************** * Name: lcd_readstatus * * Description: * Read the DDRAM address and busy bit. * ****************************************************************************/ static uint8_t lcd_readstatus(void) { uint8_t status; /* Set up to read BUSY/AD information */ putreg16(0xff, PIC32MX_IOPORTE_TRISSET); /* Set DB0-7 as inputs */ pic32mx_gpiowrite(GPIO_LCD_RS, false); /* Select command */ pic32mx_gpiowrite(GPIO_LCD_RW, true); /* Select read */ lcd_shortdelay(2); pic32mx_gpiowrite(GPIO_LCD_E, true); /* Enable transfer */ lcd_shortdelay(1); /* Read the status from the LCD */ status = (uint8_t)getreg16(PIC32MX_IOPORTE_PORT); lcd_shortdelay(1); pic32mx_gpiowrite(GPIO_LCD_E, false); return status; } /**************************************************************************** * Name: lcd_waitbusy * * Description: * Check LCD status and wait until the BUSY flag is no long set. * ****************************************************************************/ static void lcd_waitbusy(void) { while ((lcd_readstatus() & HD4478OU_BF) != 0); } /**************************************************************************** * 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 *filp, 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 * (filp->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 *filp, 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 *filp, 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 = CONFIG_LCD_MAXCONTRAST; attr->maxbrightness = CONFIG_LCD_MAXPOWER; } 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; /* SLCDIOC_GETBRIGHTNESS: Get the current brightness setting * * argument: Pointer type int that will receive the current brightness * setting */ case SLCDIOC_GETBRIGHTNESS: { FAR int *brightness = (FAR int *)((uintptr_t)arg); if (!brightness) { return -EINVAL; } *brightness = (int)g_lcd1602.brightness; lcdvdbg("SLCDIOC_GETCONTRAST: brightness=%d\n", *brightness); } break; /* SLCDIOC_SETBRIGHTNESS: Set the brightness to a new value * * argument: The new brightness value */ case SLCDIOC_SETBRIGHTNESS: { lcdvdbg("SLCDIOC_SETCONTRAST: arg=%ld\n", arg); if (arg > CONFIG_LCD_MAXPOWER) { return -ERANGE; } lcd_brightness((uint8_t)arg); } 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 */ default: return -ENOTTY; } return OK; } /**************************************************************************** * Name: lcd_poll ****************************************************************************/ #ifndef CONFIG_DISABLE_POLL static int lcd_poll(FAR struct file *filp, 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) { int ret = OK; /* Only initialize the driver once. */ if (!g_lcd1602.initialized) { lcdvdbg("Initializing\n"); /* Configure GPIO pins */ putreg16(0, PIC32MX_IOPORTE_TRIS); /* Set DB0-15 as outputs */ pic32mx_configgpio(GPIO_LCD_RS); /* RS: Selects commnand or data */ pic32mx_configgpio(GPIO_LCD_RW); /* RW: Selects read or write */ pic32mx_configgpio(GPIO_LCD_E); /* E: Starts transfer */ /* Configure LCD power in the OFF state */ pic32mx_configgpio(GPIO_LCD_LIGHT); /* K */ pic32mx_configgpio(GPIO_LCD_COMP); /* Vo */ pic32mx_configgpio(GPIO_LCD_PWR); /* Vbuson/AN5/RB5 controls +5V USB */ g_lcd1602.brightness = 0; /* Remember tht the light is off */ /* A small delay is necessary between when GPIO_LCD_E was set up as an * output with initial value of 0 and this operation. That delay should * be well covered by the intervening GPIO configurations. */ pic32mx_gpiowrite(GPIO_LCD_E, true); /* Enable transfer */ /* Configure and enable the LCD */ /* Delay for 4.1MS or more */ up_mdelay(5); /* Select the 8-bit interface. BF cannot be checked before this command. * This needs to be done a few times with some magic delays. * * Function set: 5x7 Style | N=2R | DL=8D */ lcd_wrcommand(HD4478OU_FUNC | HD4478OU_FUNC_F5x7 | HD4478OU_FUNC_N1 | HD4478OU_FUNC_DL8D); up_udelay(100); /* Delay more than 100uS */ lcd_wrcommand(HD4478OU_FUNC | HD4478OU_FUNC_F5x7 | HD4478OU_FUNC_N1 | HD4478OU_FUNC_DL8D); up_udelay(40); /* Delay more than 40uS */ lcd_wrcommand(HD4478OU_FUNC | HD4478OU_FUNC_F5x7 | HD4478OU_FUNC_N1 | HD4478OU_FUNC_DL8D); lcd_waitbusy(); lcd_wrcommand(HD4478OU_FUNC | HD4478OU_FUNC_F5x7 | HD4478OU_FUNC_N1 | HD4478OU_FUNC_DL8D); lcd_waitbusy(); /* Display ON, cursor OFF, blink OFF */ lcd_wrcommand(HD4478OU_DISPLAY | HD4478OU_DISPLAY_ON); lcd_waitbusy(); /* Clear the display and home the cursor */ lcd_wrcommand(HD4478OU_CLEAR); /* Clear display */ lcd_waitbusy(); lcd_wrcommand(HD4478OU_RETURN); /* Return home: AC=0 */ lcd_waitbusy(); /* Entry Mode Set: * * - Increment address by one, * - Shift cursor to right (display is not shifted) */ lcd_wrcommand(HD4478OU_INPUT | HD4478OU_INPUT_INCR); /* 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 */