diff options
-rw-r--r-- | drv_picoLCDGraphic.c | 613 |
1 files changed, 613 insertions, 0 deletions
diff --git a/drv_picoLCDGraphic.c b/drv_picoLCDGraphic.c new file mode 100644 index 0000000..2b88f29 --- /dev/null +++ b/drv_picoLCDGraphic.c @@ -0,0 +1,613 @@ +/* $Id: drv_picoLCDGraphic.c 840 2007-09-09 12:17:42Z michael $ + * $URL: https://ssl.bulix.org/svn/lcd4linux/trunk/drv_picoLCDGraphic.c $ + * + * driver for picoLCD Graphic(256x64) displays from mini-box.com + * + * Copyright (C) 2005 Michael Reinelt <michael@reinelt.co.at> + * Copyright (C) 2005, 2006, 2007 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net> + * + * Copyright (C) 2008 Nicu Pavel, Mini-Box.com <npavel@mini-box.com> + * + * This file is part of LCD4Linux. + * + * LCD4Linux is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * LCD4Linux is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * + * exported fuctions: + * + * struct DRIVER drv_picoLCDGraphic + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <termios.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/time.h> + +#include <usb.h> + +#include "debug.h" +#include "cfg.h" +#include "qprintf.h" +#include "udelay.h" +#include "plugin.h" +#include "widget.h" +#include "widget_text.h" +#include "widget_icon.h" +#include "widget_bar.h" +#include "drv.h" +#include "drv_generic_gpio.h" +#include "drv_generic_keypad.h" +#include "drv_generic_graphic.h" + + + +#define picoLCD_VENDOR 0x04d8 +#define picoLCD_DEVICE 0xc002 + +#define OUT_REPORT_LED_STATE 0x81 +#define OUT_REPORT_LCD_BACKLIGHT 0x91 +#define OUT_REPORT_LCD_CONTRAST 0x92 + +#define OUT_REPORT_CMD 0x94 +#define OUT_REPORT_DATA 0x95 +#define OUT_REPORT_CMD_DATA 0x96 + +#define SCREEN_H 64 +#define SCREEN_W 256 + + +#if 1 +#define DEBUG(x) debug("%s(): %s", __FUNCTION__, x); +#else +#define DEBUG(x) +#endif + + +static char Name[] = "picoLCDGraphic"; +static unsigned char *pLG_framebuffer; + +/* used to display white text on black background or inverse */ +unsigned char inverted = 0; + +static unsigned int gpo = 0; + +static char *Buffer; +static char *BufPtr; + +static usb_dev_handle *lcd; +extern int usb_debug; + + + +/****************************************/ +/*** hardware dependant functions ***/ +/****************************************/ + +static int drv_pLG_open(void) +{ + struct usb_bus *busses, *bus; + struct usb_device *dev; + char driver[1024]; + char product[1024]; + char manufacturer[1024]; + char serialnumber[1024]; + int ret; + + lcd = NULL; + + info("%s: scanning for picoLCD 256x64...", Name); + + usb_debug = 0; + + usb_init(); + usb_find_busses(); + usb_find_devices(); + busses = usb_get_busses(); + + for (bus = busses; bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + if ((dev->descriptor.idVendor == picoLCD_VENDOR) && (dev->descriptor.idProduct == picoLCD_DEVICE)) { + + info("%s: found picoLCD on bus %s device %s", Name, bus->dirname, dev->filename); + + lcd = usb_open(dev); + + ret = usb_get_driver_np(lcd, 0, driver, sizeof(driver)); + + if (ret == 0) { + info("%s: interface 0 already claimed by '%s'", Name, driver); + info("%s: attempting to detach driver...", Name); + if (usb_detach_kernel_driver_np(lcd, 0) < 0) { + error("%s: usb_detach_kernel_driver_np() failed!", Name); + return -1; + } + } + + usb_set_configuration(lcd, 1); + usleep(100); + + if (usb_claim_interface(lcd, 0) < 0) { + error("%s: usb_claim_interface() failed!", Name); + return -1; + } + + usb_set_altinterface(lcd, 0); + + usb_get_string_simple(lcd, dev->descriptor.iProduct, product, sizeof(product)); + usb_get_string_simple(lcd, dev->descriptor.iManufacturer, manufacturer, sizeof(manufacturer)); + usb_get_string_simple(lcd, dev->descriptor.iSerialNumber, serialnumber, sizeof(serialnumber)); + + info("%s: Manufacturer='%s' Product='%s' SerialNumber='%s'", Name, manufacturer, product, serialnumber); + + return 0; + } + } + } + error("%s: could not find a picoLCD", Name); + return -1; +} + + +static void drv_pLG_send(unsigned char *data, int size) +{ + int ret; + ret = usb_interrupt_write(lcd, USB_ENDPOINT_OUT + 1, (char *) data, size, 1000); + //fprintf(stderr, "%s written %d bytes\n", __FUNCTION__, ret); +} + +static int drv_pLG_close(void) +{ + usb_release_interface(lcd, 0); + usb_close(lcd); + + return 0; +} + +static void drv_pLG_update_img() +{ + unsigned char cmd3[64] = { OUT_REPORT_CMD_DATA }; /* send command + data */ + unsigned char cmd4[64] = { OUT_REPORT_DATA }; /* send data only */ + + int index, bit, x, y; + unsigned char cs, line; + unsigned char pixel; + + info("In %s\n", __FUNCTION__); + + + for (cs = 0; cs < 4; cs++) { + unsigned char chipsel = (cs << 2); //chipselect + for (line = 0; line < 8; line++) { + //ha64_1.setHIDPkt(OUT_REPORT_CMD_DATA, 8+3+32, 8, chipsel, 0x02, 0x00, 0x00, 0xb8|j, 0x00, 0x00, 0x40); + cmd3[0] = OUT_REPORT_CMD_DATA; + cmd3[1] = chipsel; + cmd3[2] = 0x02; + cmd3[3] = 0x00; + cmd3[4] = 0x00; + cmd3[5] = 0xb8 | line; + cmd3[6] = 0x00; + cmd3[7] = 0x00; + cmd3[8] = 0x40; + cmd3[9] = 0x00; + cmd3[10] = 0x00; + cmd3[11] = 32; + + //ha64_2.setHIDPkt(OUT_REPORT_DATA, 4+32, 4, chipsel | 0x01, 0x00, 0x00, 32); + + cmd4[0] = OUT_REPORT_DATA; + cmd4[1] = chipsel | 0x01; + cmd4[2] = 0x00; + cmd4[3] = 0x00; + cmd4[4] = 32; + + for (index = 0; index < 32; index++) { + pixel = 0x00; + + for (bit = 0; bit < 8; bit++) { + x = cs * 64 + index; + y = (line * 8 + bit + 0) % SCREEN_H; + + if (pLG_framebuffer[y * 256 + x] ^ inverted) + pixel |= (1 << bit); + } + cmd3[12 + index] = pixel; + } + + for (index = 32; index < 64; index++) { + pixel = 0x00; + + for (bit = 0; bit < 8; bit++) { + x = cs * 64 + index; + y = (line * 8 + bit + 0) % SCREEN_H; + if (pLG_framebuffer[y * 256 + x] ^ inverted) + pixel |= (1 << bit); + } + + cmd4[5 + (index - 32)] = pixel; + } + + drv_pLG_send(cmd3, 44); + drv_pLG_send(cmd4, 38); + } + } + + //drv_pLG_clear(); +} + + +/* for graphic displays only */ +static void drv_pLG_blit(const int row, const int col, const int height, const int width) +{ + int r, c; + + //DEBUG(fprintf(stderr, "In %s called with row %d col %d height %d width %d\n", __FUNCTION__, row, col, height, width)); + + for (r = row; r < row + height; r++) { + for (c = col; c < col + width; c++) { + pLG_framebuffer[r * 256 + c] = drv_generic_graphic_black(r, c); + //fprintf(stderr, "%d", pLG_framebuffer[r * 256 + c]); + } + //fprintf(stderr, "\n"); + } + + /* + for (r = 0; r < 64; r++) { + for(c = 0; c < 256; c++) { + fprintf(stderr, "%d", pLG_framebuffer[r * 256 + c]); + } + fprintf(stderr, "\n"); + } + */ + drv_pLG_update_img(); +} + + +void drv_pLG_clear(void) +{ + unsigned char cmd[3] = { 0x93, 0x01, 0x00 }; /* init display */ + unsigned char cmd2[9] = { OUT_REPORT_CMD }; /* init display */ + unsigned char cmd3[64] = { OUT_REPORT_CMD_DATA }; /* clear screen */ + unsigned char cmd4[64] = { OUT_REPORT_CMD_DATA }; /* clear screen */ + + int init, index; + unsigned char cs, line; + + + info("In %s\n", __FUNCTION__); + drv_pLG_send(cmd, 3); + + for (init = 0; init < 4; init++) { + unsigned char cs = ((init << 2) & 0xFF); + + cmd2[0] = OUT_REPORT_CMD; + cmd2[1] = cs; + cmd2[2] = 0x02; + cmd2[3] = 0x00; + cmd2[4] = 0x64; + cmd2[5] = 0x3F; + cmd2[6] = 0x00; + cmd2[7] = 0x64; + cmd2[8] = 0xC0; + + drv_pLG_send(cmd2, 9); + } + + + for (cs = 0; cs < 4; cs++) { + unsigned char chipsel = (cs << 2); //chipselect + for (line = 0; line < 8; line++) { + //ha64_1.setHIDPkt(OUT_REPORT_CMD_DATA, 8+3+32, 8, cs, 0x02, 0x00, 0x00, 0xb8|j, 0x00, 0x00, 0x40); + cmd3[0] = OUT_REPORT_CMD_DATA; + cmd3[1] = chipsel; + cmd3[2] = 0x02; + cmd3[3] = 0x00; + cmd3[4] = 0x00; + cmd3[5] = 0xb8 | line; + cmd3[6] = 0x00; + cmd3[7] = 0x00; + cmd3[8] = 0x40; + cmd3[9] = 0x00; + cmd3[10] = 0x00; + cmd3[11] = 32; + + unsigned char temp = 0; + + for (index = 0; index < 32; index++) { + cmd3[12 + index] = temp; + } + + drv_pLG_send(cmd3, 64); + + //ha64_2.setHIDPkt(OUT_REPORT_DATA, 4+32, 4, cs | 0x01, 0x00, 0x00, 32); + + cmd4[0] = OUT_REPORT_DATA; + cmd4[1] = chipsel | 0x01; + cmd4[2] = 0x00; + cmd4[3] = 0x00; + cmd4[4] = 32; + + for (index = 32; index < 64; index++) { + temp = 0x00; + cmd4[5 + (index - 32)] = temp; + } + drv_pLG_send(cmd4, 64); + } + } +} + +static int drv_pLG_contrast(int contrast) +{ + unsigned char cmd[2] = { 0x92 }; /* set contrast */ + + if (contrast < 0) + contrast = 0; + if (contrast > 255) + contrast = 255; + + cmd[1] = contrast; + drv_pLG_send(cmd, 2); + + return contrast; +} + + +static int drv_pLG_backlight(int backlight) +{ + unsigned char cmd[2] = { 0x91 }; /* set backlight */ + + if (backlight < 0) + backlight = 0; + if (backlight >= 1) + backlight = 200; + + cmd[1] = backlight; + drv_pLG_send(cmd, 2); + + return backlight; +} + +static int drv_pLG_gpo(int num, int val) +{ + unsigned char cmd[2] = { 0x81 }; /* set GPO */ + + if (num < 0) + num = 0; + if (num > 7) + num = 7; + + if (val < 0) + val = 0; + if (val > 1) + val = 1; + + /* set led bit to 1 or 0 */ + if (val) + gpo |= 1 << num; + else + gpo &= ~(1 << num); + + cmd[1] = gpo; + //drv_pLG_send(cmd, 2); + + return val; +} + +static int drv_pLG_start(const char *section, const int quiet) +{ + int rows = -1, cols = -1; + int value; + char *s; + + s = cfg_get(section, "Size", NULL); + if (s == NULL || *s == '\0') { + error("%s: no '%s.Size' entry from %s", Name, section, cfg_source()); + return -1; + } + if (sscanf(s, "%dx%d", &cols, &rows) != 2 || rows < 1 || cols < 1) { + error("%s: bad %s.Size '%s' from %s", Name, section, s, cfg_source()); + free(s); + return -1; + } + + if (cfg_number(section, "Inverted", 0, 0, 1, &value) > 0) { + info("Setting display inverted to %d", value); + inverted = value; + } + + DROWS = SCREEN_H; + DCOLS = SCREEN_W; + + if (drv_pLG_open() < 0) { + return -1; + } + + /* Init the command buffer */ + Buffer = (char *) malloc(1024); + if (Buffer == NULL) { + error("%s: coommand buffer could not be allocated: malloc() failed", Name); + return -1; + } + BufPtr = Buffer; + + /* Init framebuffer buffer */ + pLG_framebuffer = malloc(SCREEN_W * SCREEN_H * sizeof(unsigned char)); + if (!pLG_framebuffer) + return -1; + + DEBUG("allocated"); + memset(pLG_framebuffer, 0, SCREEN_W * SCREEN_H); + DEBUG("zeroed"); + + if (cfg_number(section, "Contrast", 0, 0, 255, &value) > 0) { + info("Setting contrast to %d", value); + drv_pLG_contrast(value); + } + + if (cfg_number(section, "Backlight", 0, 0, 1, &value) > 0) { + info("Setting backlight to %d", value); + drv_pLG_backlight(value); + } + + drv_pLG_clear(); /* clear display */ + + if (!quiet) { + char buffer[40]; + qprintf(buffer, sizeof(buffer), "%s %dx%d", Name, SCREEN_W, SCREEN_H); + if (drv_generic_graphic_greet(buffer, "http://www.picolcd.com")) { + sleep(3); + drv_pLG_clear(); + } + } + + return 0; +} + + +/****************************************/ +/*** plugins ***/ +/****************************************/ + +static void plugin_contrast(RESULT * result, RESULT * arg1) +{ + double contrast; + + contrast = drv_pLG_contrast(R2N(arg1)); + SetResult(&result, R_NUMBER, &contrast); +} + +static void plugin_backlight(RESULT * result, RESULT * arg1) +{ + double backlight; + + backlight = drv_pLG_backlight(R2N(arg1)); + SetResult(&result, R_NUMBER, &backlight); +} + +static void plugin_gpo(RESULT * result, RESULT * argv[]) +{ + double gpo; + gpo = drv_pLG_gpo(R2N(argv[0]), R2N(argv[1])); + SetResult(&result, R_NUMBER, &gpo); +} + + +/****************************************/ +/*** widget callbacks ***/ +/****************************************/ + + +/* using drv_generic_text_draw(W) */ +/* using drv_generic_text_icon_draw(W) */ +/* using drv_generic_text_bar_draw(W) */ + + +/****************************************/ +/*** exported functions ***/ +/****************************************/ + + +/* list models */ +int drv_pLG_list(void) +{ + printf("picoLCD 256x64 Graphic LCD"); + return 0; +} + + +/* initialize driver & display */ +int drv_pLG_init(const char *section, const int quiet) +{ + int ret; + + info("%s: %s", Name, "$Rev: 840 $"); + + info("PICOLCD Graphic initialization\n"); + + /* display preferences */ + XRES = 6; /* pixel width of one char */ + YRES = 8; /* pixel height of one char */ + GPOS = 8; + + /* real worker functions */ + drv_generic_graphic_real_blit = drv_pLG_blit; + + /* start display */ + if ((ret = drv_pLG_start(section, quiet)) != 0) + return ret; + + + /* initialize generic graphic driver */ + if ((ret = drv_generic_graphic_init(section, Name)) != 0) + return ret; + + /* GPO's init */ + + if ((ret = drv_generic_gpio_init(section, Name)) != 0) + return ret; + + /* register plugins */ + + AddFunction("LCD::contrast", -1, plugin_contrast); + AddFunction("LCD::backlight", -1, plugin_backlight); + AddFunction("LCD::gpo", -1, plugin_gpo); + + return 0; +} + + +/* close driver & display */ +int drv_pLG_quit(const int quiet) +{ + + info("%s: shutting down.", Name); + + drv_generic_graphic_quit(); + + /* clear display */ + drv_pLG_clear(); + + /* say goodbye... */ + if (!quiet) { + drv_generic_graphic_greet("goodbye!", NULL); + } + + drv_pLG_close(); + + if (Buffer) { + free(Buffer); + BufPtr = NULL; + } + + return (0); +} + + +DRIVER drv_picoLCDGraphic = { + .name = Name, + .list = drv_pLG_list, + .init = drv_pLG_init, + .quit = drv_pLG_quit, +}; |