/** \file server/drivers/hd44780-rpi.c * \c 4bit connection type of \c hd44780 driver for Hitachi HD44780 based LCD displays. * * The LCD is operated in its 4 bit-mode to be connected to the gpio pins on the Raspberry Pi. */ /** Copyright (c) 2012 Paul Corner * 2000, 1999, 1995 Benjamin Tse * 2001 Joris Robijn * 1999 Andrew McMeikan * 1998 Richard Rognlie * 1997 Matthias Prinke * * Based in part on the hd44780-4bit driver and comments/notes/code provided * by Serac (RaspberryPi forum user) * * This file is released under the GNU General Public License. Refer to the * COPYING file distributed with this package. */ /** * The code to access the gpio on a Raspberry Pi draws on an * example program dated 15-January-2012 by Dom and Gert van Loo: * How to access GPIO registers from C-code on the Raspberry-Pi * The default connections are: * header (gpio) LCD * P1-12 (18) D7 (14) * P1-16 (23) D6 (13) * P1-18 (24) D5 (12) * P1-22 (25) D4 (11) * P1-24 (8) EN (6) * P1-26 (7) RS (4) * * Mappings can be set in the config file using the key-words: * ENABLE, RS, D7, D6, D5, D4 in the [HD44780] section. */ #define D7 18 #define D6 23 #define D5 24 #define D4 25 #define RS 7 #define EN 8 /** * * RW (5) on the LCD MUST be hard wired low to prevent 5V logic appearing * on the gpio pins. * * Only a single LCD is currently supported - That may change along with * i2c connected devices. */ #include "hd44780-rpi.h" #include "hd44780-low.h" #include "port.h" #include "report.h" #include #include #include #include #include #include #include #include #include #include #include // Generally, any function that accesses the LCD control lines needs to be // implemented separately for each HW design. This is typically (but not // restricted to): // HD44780_senddata // HD44780_readkeypad void lcdrpi_HD44780_senddata(PrivateData *p, unsigned char displayID, unsigned char flags, unsigned char ch); // To be implemented at some point if required.. void lcdrpi_HD44780_backlight(PrivateData *p, unsigned char state); unsigned char lcdrpi_HD44780_readkeypad(PrivateData *p, unsigned int YData); static volatile unsigned int *gpio_map = NULL; static int setup_io(Driver *drvthis) { void *gpio_mem; int mem_fd = 0; /* open /dev/mem */ if (gpio_map != NULL) { report(RPT_ERR, "IO already in use."); return -1; } if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { report(RPT_ERR, "can not open /dev/mem"); return -1; } if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) { report(RPT_ERR, "memory allocation error"); return -1; } if ((unsigned int)gpio_mem % PAGE_SIZE) gpio_mem += PAGE_SIZE - ((unsigned int)gpio_mem % PAGE_SIZE); gpio_map = (unsigned int *)mmap((caddr_t)gpio_mem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, mem_fd, GPIO_BASE); if ((long)gpio_map < 0) { report(RPT_ERR, "mmap error %d", (int)gpio_map); return -1; } close(mem_fd); // Done with mem_fd and don't need to use it again. return 0; } /* gpio pins 3, 5, 27 available on S5 */ static const int gpio_pins[] = { 0, 1, -1, -1, 4, -1, -1, 7, 8, 9, 10, 11, -1, -1, 14, 15, -1, 17, 18, -1, -1, 21, 22, 23, /* 8-31 accessible via board-id resistors R3-R10 */ 24, 25, -1, -1, -28, -29, -30, -31 }; static int setup_gpio(int gpio) { volatile int i; if (gpio > 31 || gpio_pins[gpio] < 0) { printf("%s: Invalid gpio pin %i - Mapped to %i\n", __FUNCTION__, gpio, gpio_pins[gpio]); return -1; } printf("%s: gpio pin %i - Mapped to %i\n", __FUNCTION__, gpio, gpio_pins[gpio]); *(gpio_map+0x0025) &= ~3; /* After writing to the GPPUD register, need to wait 150 cycles as per p101 BCM2835.pdf. The following while loop uses approx five instructions plus another two to load the counter. Note: the int must be volatile or gcc will optimise loop out. */ i=30; while (--i); // need a 150 cycle min wait *(gpio_map+0x0026+(gpio/32)) = 1<<(gpio%32); // Another 150 cycle wait required after a write to GPPUDCLK i=30; while (--i); *(gpio_map+0x0025) &= ~3; *(gpio_map+0x0026+(gpio/32)) = 0; *(gpio_map+(gpio/10)) = (*(gpio_map+(gpio/10))&~(7<<((gpio%10)*3)))|(1<<((gpio%10)*3)); return 0; } static void cleanup_io(PrivateData *p) { INP_GPIO(p->rpi_gpio->enable); INP_GPIO(p->rpi_gpio->rs); INP_GPIO(p->rpi_gpio->d7); INP_GPIO(p->rpi_gpio->d6); INP_GPIO(p->rpi_gpio->d5); INP_GPIO(p->rpi_gpio->d4); munmap((caddr_t)gpio_map, BLOCK_SIZE); free(p->rpi_gpio); return; } /** * Initialize the driver. * \param drvthis Pointer to driver structure. * \retval 0 Success. * \retval -1 Error. */ int hd_init_rpi(Driver *drvthis) { PrivateData *p = (PrivateData*) drvthis->private_data; if (setup_io(drvthis) < 0) { report(RPT_ERR, "Failed to set up gpio."); return -1; } p->rpi_gpio = malloc(sizeof(struct rpi_gpio_map)); if (p->rpi_gpio == NULL) { report(RPT_ERR, "%s: unable to allocate memory", drvthis->name); return -1; } p->rpi_gpio->enable = drvthis->config_get_int(drvthis->name, "EN", 0, EN); p->rpi_gpio->rs = drvthis->config_get_int(drvthis->name, "RS", 0, RS); p->rpi_gpio->d7 = drvthis->config_get_int(drvthis->name, "D7", 0, D7); p->rpi_gpio->d6 = drvthis->config_get_int(drvthis->name, "D6", 0, D6); p->rpi_gpio->d5 = drvthis->config_get_int(drvthis->name, "D5", 0, D5); p->rpi_gpio->d4 = drvthis->config_get_int(drvthis->name, "D4", 0, D4); if (p->rpi_gpio->enable == p->rpi_gpio->rs || p->rpi_gpio->enable == p->rpi_gpio->d7 || p->rpi_gpio->enable == p->rpi_gpio->d6 || p->rpi_gpio->enable == p->rpi_gpio->d5 || p->rpi_gpio->enable == p->rpi_gpio->d4 || p->rpi_gpio->rs == p->rpi_gpio->d7 || p->rpi_gpio->rs == p->rpi_gpio->d6 || p->rpi_gpio->rs == p->rpi_gpio->d5 || p->rpi_gpio->rs == p->rpi_gpio->d4 || p->rpi_gpio->d7 == p->rpi_gpio->d6 || p->rpi_gpio->d7 == p->rpi_gpio->d5 || p->rpi_gpio->d7 == p->rpi_gpio->d4 || p->rpi_gpio->d6 == p->rpi_gpio->d5 || p->rpi_gpio->d6 == p->rpi_gpio->d4 || p->rpi_gpio->d5 == p->rpi_gpio->d4) { report(RPT_ERR, "Can not use the same GPIO pin twice\n"); return -1; } if (setup_gpio(p->rpi_gpio->enable) || setup_gpio(p->rpi_gpio->rs) || setup_gpio(p->rpi_gpio->d7) || setup_gpio(p->rpi_gpio->d6) || setup_gpio(p->rpi_gpio->d5) || setup_gpio(p->rpi_gpio->d4)) { report(RPT_ERR, "Invalid GPIO specified\n"); return -1; } p->hd44780_functions->senddata = lcdrpi_HD44780_senddata; // Next two are stubbed functions. p->hd44780_functions->backlight = lcdrpi_HD44780_backlight; p->hd44780_functions->readkeypad = lcdrpi_HD44780_readkeypad; p->hd44780_functions->close = cleanup_io; // setup the lcd in 4 bit mode p->hd44780_functions->senddata(p, 0, RS_INSTR, /*FUNCSET | IF_8BIT*/ 0x33); p->hd44780_functions->uPause(p, 4100); p->hd44780_functions->senddata(p, 0, RS_INSTR, /*FUNCSET | IF_4BIT*/ 0x32); p->hd44780_functions->uPause(p, 150); common_init(p, IF_4BIT); return 0; } /** * Send data or commands to the display. * \param p Pointer to driver's private data structure. * \param displayID ID of the display (or 0 for all) to send data to. * \param flags Defines whether to end a command or data. * \param ch The value to send. */ void lcdrpi_HD44780_senddata(PrivateData *p, unsigned char displayID, unsigned char flags, unsigned char ch) { if (displayID > 1) { return; } if (gpio_map == NULL) { printf("gpio not set up.\n"); return; } if (flags == RS_INSTR) { SET_GPIO(p->rpi_gpio->rs, 0); } else { // flags == RS_DATA SET_GPIO(p->rpi_gpio->rs, 1); } // Clear data lines ready for nibbles SET_GPIO(p->rpi_gpio->d7, 0); SET_GPIO(p->rpi_gpio->d6, 0); SET_GPIO(p->rpi_gpio->d5, 0); SET_GPIO(p->rpi_gpio->d4, 0); p->hd44780_functions->uPause(p, 50); // Output upper nibble first SET_GPIO(p->rpi_gpio->d7, (ch & 0x80)); SET_GPIO(p->rpi_gpio->d6, (ch & 0x40)); SET_GPIO(p->rpi_gpio->d5, (ch & 0x20)); SET_GPIO(p->rpi_gpio->d4, (ch & 0x10)); p->hd44780_functions->uPause(p, 50); // Data is clocked on the falling edge of EN SET_GPIO(p->rpi_gpio->enable, 1); p->hd44780_functions->uPause(p, 50); SET_GPIO(p->rpi_gpio->enable, 0); p->hd44780_functions->uPause(p, 50); // Do same for lower nibble SET_GPIO(p->rpi_gpio->d7, 0); SET_GPIO(p->rpi_gpio->d6, 0); SET_GPIO(p->rpi_gpio->d5, 0); SET_GPIO(p->rpi_gpio->d4, 0); p->hd44780_functions->uPause(p, 50); SET_GPIO(p->rpi_gpio->d7, (ch & 0x08)); SET_GPIO(p->rpi_gpio->d6, (ch & 0x04)); SET_GPIO(p->rpi_gpio->d5, (ch & 0x02)); SET_GPIO(p->rpi_gpio->d4, (ch & 0x01)); p->hd44780_functions->uPause(p, 50); SET_GPIO(p->rpi_gpio->enable, 1); p->hd44780_functions->uPause(p, 50); SET_GPIO(p->rpi_gpio->enable, 0); p->hd44780_functions->uPause(p, 50); return; } /** * Turn display backlight on or off. * \param p Pointer to driver's private data structure. * \param state New backlight status. */ void lcdrpi_HD44780_backlight(PrivateData *p, unsigned char state) { return; } /** * Read keypress. * \param p Pointer to driver's private data structure. * \param YData Bitmap of rows / lines to enable. * \return Bitmap of the pressed keys. */ unsigned char lcdrpi_HD44780_readkeypad(PrivateData *p, unsigned int YData) { return 0; }