diff options
Diffstat (limited to '')
-rw-r--r-- | Crystalfontz.c | 545 | ||||
-rw-r--r-- | Crystalfontz.h | 69 | ||||
-rw-r--r-- | README.Crystalfontz | 44 |
3 files changed, 658 insertions, 0 deletions
diff --git a/Crystalfontz.c b/Crystalfontz.c new file mode 100644 index 0000000..377382b --- /dev/null +++ b/Crystalfontz.c @@ -0,0 +1,545 @@ +/* + * driver for display modules from Crystalfontz + * + * Copyright 2000 by Herbert Rosmanith (herp@wildsau.idv.uni-linz.ac.at) + * + * This program 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. + * + * This program 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. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <termios.h> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> + +#include "cfg.h" +#include "lock.h" +#include "display.h" +#include "Crystalfontz.h" + +#define XRES 6 +#define YRES 8 +#define BARS ( BAR_L | BAR_R ) + +static LCD Lcd; +static char *Port=NULL; +static speed_t Speed; +static int Device=-1; + +static char *Txtbuf,*BackupTxtbuf; /* text (+backup) buffer */ +static char *Barbuf,*BackupBarbuf; /* bar (+backup) buffer */ +static char *CustCharMap; +static int tdim,bdim; /* text/bar dimension */ +static char isTxtDirty; +static char *isBarDirty; +static char isAnyBarDirty; + +static void cryfonquit() { + + close(Device); + unlock_port(Port); + exit(0); +} + +static int cryfonopen() { +int fd; +pid_t pid; +struct termios portset; + + if ((pid=lock_port(Port))!=0) { + if (pid==-1) fprintf(stderr,"Crystalfontz: port %s could not be locked\n",Port); + else fprintf(stderr,"Crystalfontz: port %s is locked by process %d\n",Port,pid); + return -1; + } + fd=open(Port,O_RDWR|O_NOCTTY|O_NDELAY); + if (fd==-1) { + fprintf(stderr,"Crystalfontz: open(%s) failed: %s\n", + Port,strerror(errno)); + return -1; + } + if (tcgetattr(fd,&portset)==-1) { + fprintf(stderr,"Crystalfontz: tcgetattr(%s) failed: %s\n", + Port,strerror(errno)); + return -1; + } + cfmakeraw(&portset); + cfsetospeed(&portset,Speed); + if (tcsetattr(fd, TCSANOW, &portset)==-1) { + fprintf(stderr,"Crystalfontz: tcsetattr(%s) failed: %s\n", + Port,strerror(errno)); + return -1; + } + return fd; +} + +static int cryfoninit(LCD *Self) { +char *port; +char *speed; +char *backlight; +char *contrast; +char cmd_backlight[2]={ CRYFON_BACKLIGHT_CTRL, }; +char cmd_contrast[2]={ CRYFON_CONTRAST_CTRL, }; + + Lcd=*Self; + + if (Port) { + free(Port); + Port=NULL; + } + + if ((port=cfg_get("Port"))==NULL || *port=='\0') { + fprintf(stderr,"CrystalFontz: no 'Port' entry in %s\n", + cfg_file()); + return -1; + } + if (port[0]=='/') Port=strdup(port); + else { + Port=(char *)malloc(5/*/dev/ */+strlen(port)+1); + sprintf(Port,"/dev/%s",port); + } + + speed=cfg_get("Speed")?:"9600"; + switch(atoi(speed)) { + case 1200: + Speed=B1200; + break; + case 2400: + Speed=B2400; + break; + case 9600: + Speed=B9600; + break; + case 19200: + Speed=B19200; + break; + default: + fprintf(stderr,"CrystalFontz: unsupported speed '%s' in '%s'\n", + speed,cfg_file()); + return -1; + } + + if ((Device=cryfonopen())==-1) + return -1; + + tdim=Lcd.rows*Lcd.cols; + bdim=Lcd.rows*Lcd.cols*2; + + Txtbuf=(char *)malloc(tdim); + if (Txtbuf==NULL) { + fprintf(stderr,"CrystalFontz: out of memory\n"); + return -1; + } + CustCharMap=(char *)malloc(tdim); + if (CustCharMap==NULL) { + fprintf(stderr,"CrystalFontz: out of memory\n"); + return -1; + } + BackupTxtbuf=(char *)malloc(tdim); + if (BackupTxtbuf==NULL) { + fprintf(stderr,"CrystalFontz: out of memory\n"); + return -1; + } + Barbuf=(char *)malloc(bdim); + if (Barbuf==NULL) { + fprintf(stderr,"CrystalFontz: out of memory\n"); + return -1; + } + BackupBarbuf=(char *)malloc(bdim); + if (BackupBarbuf==NULL) { + fprintf(stderr,"CrystalFontz: out of memory\n"); + return -1; + } + isBarDirty=(char *)malloc(Lcd.rows); + if (isBarDirty==NULL) { + fprintf(stderr,"CrystalFontz: out of memory\n"); + return -1; + } + memset(Txtbuf,' ',tdim); + memset(CustCharMap,-1,tdim); + memset(BackupTxtbuf,255,tdim); + memset(Barbuf,0,bdim); + memset(BackupBarbuf,0,bdim); + memset(isBarDirty,0,Lcd.rows); + isAnyBarDirty=0; + isTxtDirty=0; + + signal(SIGINT,cryfonquit); + signal(SIGQUIT,cryfonquit); + signal(SIGTERM,cryfonquit); + + usleep(350000); + write(Device, CRYFON_HIDE_CURSOR CRYFON_SCROLL_OFF CRYFON_WRAP_OFF,3); + backlight=cfg_get("Backlight")?:NULL; + if (backlight) { + cmd_backlight[1]=atoi(backlight); + write(Device,cmd_backlight,4); + } + + contrast=cfg_get("Contrast")?:NULL; + if (contrast) { + cmd_contrast[1]=atoi(contrast); + write(Device,cmd_contrast,2); + } + + return 0; +} + +static int cryfonclear() { + memset(Txtbuf,' ',tdim); + memset(Barbuf,0,bdim); + return 0; +} + +static int cryfonput(int row,int col,char *text) { +int pos; + + pos=row*Lcd.cols+col; + memcpy(Txtbuf+pos,text,strlen(text)); + isTxtDirty|=memcmp(Txtbuf+pos,BackupTxtbuf+pos,strlen(text)); + return 0; +} + +static unsigned char p1[] = { 0x3f,0x1f,0x0f,0x07,0x03,0x01,0x00 }; +static unsigned char p2[] = { 0x00,0x20,0x30,0x38,0x3c,0x3e,0x3f }; + +static void blacken(int bitfrom,int len,int pos,int startbyte,int endbyte) { + + if (startbyte==endbyte) + Barbuf[pos] |= + (p1[bitfrom%XRES] & p2[1+((bitfrom+len-1)%XRES)]); + else { + int n; + Barbuf[pos] |= p1[bitfrom%XRES]; + n=endbyte-startbyte-1; + if (n>0) memset(Barbuf+pos+1,0x3f,n); + Barbuf[pos+n+1] |= p2[1+((bitfrom+len-1)%XRES)]; + + } +} + +static void whiten(int bitfrom,int len,int pos,int startbyte,int endbyte) { + + if (startbyte==endbyte) + Barbuf[pos] &= + (p2[bitfrom%XRES] | p1[1+((bitfrom+len-1)%XRES)]); + else { + int n; + Barbuf[pos] &= p2[bitfrom%XRES]; + n=endbyte-startbyte-1; + if (n>0) memset(Barbuf+pos+1,0x00,n); + Barbuf[pos+n+1] &= p1[1+((bitfrom+len-1)%XRES)]; + } +} + +static int cryfonbar(int type,int row,int col,int max,int len1,int len2) { +int endb,maxb; +int bitfrom; +int pos; + + if (len1<1) len1=1; + else if (len1>max) len1=max; + + if (len2<1) len2=1; + else if (len2>max) len2=max; + + bitfrom=col*XRES; + endb=(bitfrom+len1-1)/XRES; + pos=row*Lcd.cols*2; + + switch(type) { + case BAR_L: + blacken(bitfrom,len1,pos+col,col,endb); + endb=(bitfrom+len1)/XRES; + maxb=(bitfrom+max-1)/XRES; + whiten(bitfrom+len1,max-len1,pos+endb,endb,maxb); + if (len1==len2) + memcpy(Barbuf+pos+col+Lcd.cols, + Barbuf+pos+col, + maxb-col+1); + else { + pos+=Lcd.cols; + endb=(bitfrom+len2-1)/XRES; + blacken(bitfrom,len2,pos+col,col,endb); + endb=(bitfrom+len2)/XRES; + whiten(bitfrom+len2,max-len2,pos+endb,endb,maxb); + } + break; + case BAR_R: + blacken(bitfrom,len1,pos+col,col,endb); + endb=(bitfrom+len1)/XRES; + maxb=(bitfrom+max-1)/XRES; + whiten(bitfrom+len1,max-len1,pos+endb,endb,maxb); + if (len1==len2) + memcpy(Barbuf+pos+col+Lcd.cols, + Barbuf+pos+col, + maxb-col+1); + else { + pos+=Lcd.cols; + endb=(bitfrom+len2-1)/XRES; + blacken(bitfrom,len2,pos+col,col,endb); + endb=(bitfrom+len2)/XRES; + whiten(bitfrom+len2,max-len2,pos+endb,endb,maxb); + } + break; + } + isBarDirty[row]=1; + isAnyBarDirty=1; /* dont know exactly, check anyway */ + return 0; +} + +static int txt_lc=-1,txt_lr=-1; + +static void writeTxt(char r,char c,int itxt,int len) { +static char cmd_goto[3]=CRYFON_GOTO; + + if (txt_lr!=r || txt_lc!=c) { + if (r==0 && c==0) write(Device,CRYFON_HOME,1); + else { + cmd_goto[1]=(unsigned char)c; + cmd_goto[2]=(unsigned char)r; + write(Device,(char *)&cmd_goto,3); + } + } + txt_lr=r; + txt_lc=c+len; + write(Device,Txtbuf+itxt,len); +} + +static void writeTxtDiff() { +int spos,scol; +int i,j,k; + + k=0; + txt_lr=txt_lc=-1; + for (i=0;i<Lcd.rows;i++) { + spos=-1; + scol=0; /* make gcc happy */ + for (j=0;j<Lcd.cols;j++) { + if (Txtbuf[k]^BackupTxtbuf[k]) { + if (spos==-1) { + spos=k; + scol=j; + } + } else if (spos>-1) { + writeTxt((char)i,(char)scol,spos,k-spos); + memcpy(BackupTxtbuf+spos,Txtbuf+spos,k-spos); + spos=-1; + } + k++; + } + if (spos>-1) { + writeTxt((char)i,(char)scol,spos,k-spos); + memcpy(BackupTxtbuf+spos,Txtbuf+spos,k-spos); + } + } +} + +/* private bar flushing routines */ + +static char BarCharBuf[256]; +static int bi=0; + +static void flushBarCharBuf() { + if (bi) { + write(Device,BarCharBuf,bi); /* flush buffer */ + tcdrain(Device); + bi=0; + } +} + +static void writeBarCharBuf(char *s,int len) { + if (bi+len>=sizeof(BarCharBuf)) { + flushBarCharBuf(); + } + memcpy(BarCharBuf+bi,s,len); + bi+=len; +} + +static struct { + char use_count; /* 0 - unused */ + char data[8]; +} cust_chars[8] = { + { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, } +}; + +static int search_cust_char(char c1,char c2) { +int i; + for (i=0;i<8;i++) { + if (cust_chars[i].data[0]==c1 + && cust_chars[i].data[4]==c2) { + return i; + } + } + return -1; /* not found */ +} + +static void set_cust_char(int ci,char c1,char c2) { +static char cmd_cust[10] = CRYFON_SET_CUSTOM_CHAR_BITMAP; + + memset(cust_chars[ci].data,c1,4); + memset(cust_chars[ci].data+4,c2,4); + cmd_cust[1]=ci; + memset(cmd_cust+2,c1,4); + memset(cmd_cust+6,c2,4); + writeBarCharBuf(cmd_cust,10); +} + +static int alloc_cust_char(char c1,char c2) { +static char allzero[8]={0, }; +int i; + + /* first, try to allocate a never used entry */ + + for(i=0;i<8;i++) + if (memcmp(cust_chars[i].data,allzero,8)==0) { + set_cust_char(i,c1,c2); + return i; + } + + /* if that fails, pick an entry with is not yet in use */ + + for(i=0;i<8;i++) + if (cust_chars[i].use_count==0) { + set_cust_char(i,c1,c2); + return i; + } + return -1; /* no free char */ +} + +static void use_cust_char(int i,int j,int ci) { +int x; + x=i*Lcd.cols+j; + if (CustCharMap[x]==-1) { + cust_chars[ci].use_count++; + CustCharMap[x]=ci; + } + /* else: internal consistency failure */ + else { + printf("Crystalfontz: internal consistency failure 1\n"); + exit(0); + } +} + +static void unuse_cust_char(int i,int j) { +int ci,x; + x=i*Lcd.cols+j; + ci=CustCharMap[x]; + if (ci>-1) { + CustCharMap[x]=-1; + cust_chars[ci].use_count--; + if (cust_chars[i].use_count==-1) { + printf("Crystalfontz: internal consistency failure 2\n"); + exit(0); + } + } +} + +static int bar_lc=-1,bar_lr=-1; + + +static void writeChar(unsigned char r,unsigned char c,unsigned char ch) { +static char cmd_goto[3]=CRYFON_GOTO; + + if (bar_lr!=r || bar_lc!=c) { + if (r==0 && c==0) writeBarCharBuf(CRYFON_HOME,1); + else { + cmd_goto[1]=(unsigned char)c; + cmd_goto[2]=(unsigned char)r; + writeBarCharBuf((char *)&cmd_goto,3); + } + } + bar_lr=r; + bar_lc=c++; + writeBarCharBuf(&ch,1); +} + +static void writeBarDiff() { +char c1,c2; +int i,j,k1,k2,ci; + + for (i=0;i<Lcd.rows;i++) { + if (isBarDirty[i]) { + k1=i*Lcd.cols*2; + k2=k1+Lcd.cols; + bar_lr=bar_lc=-1; + for (j=0;j<Lcd.cols;j++) { + c1=Barbuf[k1]; + c2=Barbuf[k2]; + if (c1^BackupBarbuf[k1] || + c2^BackupBarbuf[k2]) { + + if (c1==0 && c2==0) { /* blank */ + unuse_cust_char(i,j); + writeChar(i,j,' '); + } + else if (c1==0x1f && c2==0x1f) { /*boxlike*/ + unuse_cust_char(i,j); + writeChar(i,j,0xff); + } + else { + /* search cust char */ + ci=search_cust_char(c1,c2); + if (ci>-1) { /* found: reuse that char */ + unuse_cust_char(i,j); + writeChar(i,j,128+ci); + use_cust_char(i,j,ci); + } + else { /* not found: get a new one */ + ci=alloc_cust_char(c1,c2); + if (ci>-1) { + unuse_cust_char(i,j); + writeChar(i,j,128+ci); + use_cust_char(i,j,ci); + } + else printf("failed to alloc a custom char\n"); + } + } + BackupBarbuf[k1]=c1; + BackupBarbuf[k2]=c2; + } + k1++; + k2++; + } + } + isBarDirty[i]=0; + } + flushBarCharBuf(); +} + +static int cryfonflush() { + + if (isTxtDirty) { + writeTxtDiff(); + isTxtDirty=0; + } + if (isAnyBarDirty) { + writeBarDiff(); + isAnyBarDirty=0; + } + + return 0; +} + +LCD Crystalfontz[] = { + { "626", 2, 16, XRES, YRES, BARS, cryfoninit, cryfonclear, cryfonput, cryfonbar, cryfonflush }, + { "636", 2, 16, XRES, YRES, BARS, cryfoninit, cryfonclear, cryfonput, cryfonbar, cryfonflush }, + { "632", 2, 16, XRES, YRES, BARS, cryfoninit, cryfonclear, cryfonput, cryfonbar, cryfonflush }, + { "634", 4, 20, XRES, YRES, BARS, cryfoninit, cryfonclear, cryfonput, cryfonbar, cryfonflush }, + { NULL } +}; diff --git a/Crystalfontz.h b/Crystalfontz.h new file mode 100644 index 0000000..fb77c0d --- /dev/null +++ b/Crystalfontz.h @@ -0,0 +1,69 @@ +/* + * driver for display modules from Crystalfontz + * + * Copyright 2000 by Herbert Rosmanith (herp@wildsau.idv.uni-linz.ac.at) + * + * This program 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. + * + * This program 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. + * + */ + + +/* see: 634full.pdf, avail. from www.crystalfontz.com + * values here are octal + */ + +#define CRYFON_NULL "\000" +#define CRYFON_CURSOR_HOME "\001" +#define CRYFON_HOME CRYFON_CURSOR_HOME +#define CRYFON_HIDE_DISPLAY "\002" +#define CRYFON_RESTORE_DISPLAY "\003" +#define CRYFON_HIDE_CURSOR "\004" +#define CRYFON_SHOW_UL_CURSOR "\005" +#define CRYFON_SHOW_BLK_CURSOR "\006" +#define CRYFON_SHOW_INV_CURSOR "\007" +#define CRYFON_BACKSPACE "\010" +/*not used: 9 (tab), 011 octal */ +#define CRYFON_LF "\012" +#define CRYFON_DEL_IN_PLACE "\013" +#define CRYFON_FF "\014" +#define CRYFON_CR "\015" +#define CRYFON_BACKLIGHT_CTRL "\016" +#define CRYFON_CONTRAST_CTRL "\017" +/*not used: 16, 020 octal */ +#define CRYFON_SET_CURSOR_POS "\021" +#define CRYFON_GOTO CRYFON_SET_CURSOR_POS +#define CRYFON_HORIZONTAL_BAR "\022" +#define CRYFON_SCROLL_ON "\023" +#define CRYFON_SCROLL_OFF "\024" +#define CRYFON_SET_SCROLL_MARQU "\025" +#define CRYFON_ENABLE_SCROLL_MARQU "\026" +#define CRYFON_WRAP_ON "\027" +#define CRYFON_WRAP_OFF "\030" +#define CRYFON_SET_CUSTOM_CHAR_BITMAP "\031" +#define CRYFON_REBOOT "\032" +#define CRYFON_ESC "\033" +#define CRYFON_LARGE_BLK_NUM "\034" +/*not used: 29, 035 octal*/ +#define CRYFON_SEND_DATA_DIRECT "\036" +#define CRYFON_SHOW_INFO "\037" +#define CRYFON_CUST_CHAR0 "\200" +#define CRYFON_CUST_CHAR1 "\201" +#define CRYFON_CUST_CHAR2 "\202" +#define CRYFON_CUST_CHAR3 "\203" +#define CRYFON_CUST_CHAR4 "\204" +#define CRYFON_CUST_CHAR5 "\205" +#define CRYFON_CUST_CHAR6 "\206" +#define CRYFON_CUST_CHAR7 "\207" + diff --git a/README.Crystalfontz b/README.Crystalfontz new file mode 100644 index 0000000..4c76b45 --- /dev/null +++ b/README.Crystalfontz @@ -0,0 +1,44 @@ + +This is the README file for the Crystalfontz display driver for lcd4linux + +This driver supports the 632/634 LCD-Modules from Crystalfontz, but should +work for the 626 and 636 modules too. The 634 is a 20x4 character display, +while the others only display 16x2. I've written the driver using a +634 module. + +The driver understands the following configuration parameters: + +Display: any of 626, 632, 634 and 636. + +Port: serial device (i.e. ttyS0) the LCD module is connnected + to. + +Speed: any of 1200, 2400, 9600 and 19200. By default, the driver + uses 9600 which is the speed the LCD modules are hardwired + at. If your module works at a different speed than 9600, + use this parameter. Otherwise omit it (i.e. omit it when + you have a 634). + +Backlight: controls the backlight brightness. Quote from 634.pdf from + the Crystalfonts-Webserver[1]: "0=OFF 100=ON. Intermediate + values vary the brightness. There are a total of 25 possible + brightness levels." + +Contrast: controls the contrast settings. Quote[1]: "0=very light, + 100 = very dark. 50 is typical. There are a total of 25 + possible contrast levels." + + +Known bugs: +When you draw a bar over a previously drawn textfield, the white portion +the bar will not erase the text. Only when the black portion of the bar +has reached the full bar length, the text will be erased. I did not bother +to implement that, since in lcd4linux, the whole display-screen is erased +prior to switching to a different 'screen'. Implementing this feature would +just add to program-overhead. Yes, you guessed it: I did not use the "bar"- +command that comes with the LCD-module, but wrote my own instead. +lcd4linux also supports "split-" or "dual-bars" (two bars in one segment), +which are not available on the Crystalfontz firmware. + + [1] http://www.crystalfontz.com + |