/* * sc1602.c */ #if !defined(__KERNEL__) #define __KERNEL__ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SC1602_BASE 0xA00000 #define SC1602_OFFS 8 #define SC1602_CMD (*(volatile unsigned char *)(SC1602_BASE)) #define SC1602_DATA (*(volatile unsigned char *)(SC1602_BASE+SC1602_OFFS)) #define SC1602_DEVNAME "sc1602" #define SC1602_MAJOR 201 #define SC1602_WIDTH 16 MODULE_AUTHOR( "sushi-k@quake3.jp" ); MODULE_DESCRIPTION( "AKIZUKI SC1602 LCD driver." ); MODULE_LICENSE( "GPL" ); static unsigned int sc1602_major = SC1602_MAJOR; static char *sc1602_devname = SC1602_DEVNAME; static unsigned char *msg = "sc1602.o"; static int sc1602_isbusy(void) { unsigned long timeout,now; unsigned char d; now = jiffies; timeout = now + HZ/10; while( jiffies < timeout ) { d = SC1602_CMD; if( !(d & 0x80) ) return (int) d; else { schedule(); // 他プロセスに渡す } } return -1; } // LCDからの読み出し inline int sc1602_readchar(void) { unsigned char d; if( sc1602_isbusy() < 0 ) return -1; ndelay(100); d = SC1602_DATA; return (int) d; } // LCDへの書き込み inline int sc1602_writechar(unsigned char d) { if( sc1602_isbusy() < 0 ) return -1; ndelay(100); SC1602_DATA = d; return 0; } // アドレスの取得 inline int sc1602_readaddr(void) { return sc1602_isbusy(); } // LCDコマンドの書き込み inline int sc1602_writecmd(unsigned char d) { if( sc1602_isbusy() < 0 ) return -1; ndelay(100); SC1602_CMD = d; return 0; } // open static int sc1602_open(struct inode *inode, struct file *filp) { MOD_INC_USE_COUNT; return 0; } // release static int sc1602_release(struct inode *inode, struct file *filp) { MOD_DEC_USE_COUNT; return 0; } #define K_BUFF_SIZE 0x80 // write static ssize_t sc1602_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) { unsigned char k_buff[K_BUFF_SIZE]; int lcd_pos; int copy_len , i; // 現在のアドレス読み出し lcd_pos = sc1602_readaddr(); if( lcd_pos < 0 ) return -EFAULT; // 多分ない if( lcd_pos >= K_BUFF_SIZE ) return -ENOSPC; copy_len = K_BUFF_SIZE - lcd_pos; if( copy_len > count ) copy_len = count; if( copy_from_user( k_buff, buf, copy_len) ) { printk( KERN_INFO "%s : copy_from_user failed.\n", msg ); return -EFAULT; } // LCD出力 for( i = 0; i < copy_len; i++ ) { // エスケープシーケンス if( k_buff[i] == '\e' ) { if( ++i >= copy_len ) break; switch( k_buff[i] ) { // 画面クリア case 'r': if( sc1602_writecmd( 0x01 ) < 0 ) return -EFAULT; udelay( 1650 ); // wait > 1.65ms break; // カーソル表示・ブリンクオン case 'b': if( sc1602_writecmd( 0x0F ) < 0 ) return -EFAULT; break; // カーソル表示・ブリンクオフ case 'c': if( sc1602_writecmd( 0x0E ) < 0 ) return -EFAULT; break; // カーソル表示なし case 'n': if( sc1602_writecmd( 0x0C ) < 0 ) return -EFAULT; break; default: if( sc1602_writechar( k_buff[i] ) < 0 ) return -EFAULT; } } // 改行 else if( k_buff[i] == '\n' ) { int j, cur_pos; cur_pos = sc1602_readaddr(); if( cur_pos < 0 ) return -EFAULT; // すでに2行目なら if( cur_pos > 0x3F ) { unsigned char scr_buff[SC1602_WIDTH+1]; // スクロール if( sc1602_writecmd(0xC0) < 0 ) return -EFAULT; for( j = 0 ; j < SC1602_WIDTH; j++ ) { int d; d = sc1602_readchar(); if( d < 0 ) return -EFAULT; scr_buff[j] = (unsigned char)d; } if( sc1602_writecmd( 0x80 ) < 0 ) return -EFAULT; for( j = 0; j < SC1602_WIDTH; j++ ) { if( sc1602_writechar(scr_buff[j]) < 0 ) return -EFAULT; } } // 2行目行頭にカーソルセット if( sc1602_writecmd( 0xC0 ) < 0 ) return -EFAULT; // 2行目クリア for( j = 0 ; j < SC1602_WIDTH ; j++ ) { if( sc1602_writechar(' ') < 0 ) return -EFAULT; } // 2行目行頭にカーソルセット if( sc1602_writecmd( 0xC0 ) < 0 ) return -EFAULT; } // 復帰 else if( k_buff[i] == '\r' ) { int cur_pos = sc1602_readaddr(); if( cur_pos < 0 ) return -EFAULT; if( sc1602_writecmd((cur_pos|0x80) & 0xC0) < 0 ) return -EFAULT; } // 普通の文字 else { if( sc1602_writechar( k_buff[i] ) < 0 ) return -EFAULT; } } return i; } struct file_operations sc1602_fops = { owner: THIS_MODULE, read: NULL, write: sc1602_write, open: sc1602_open, release: sc1602_release, ioctl: NULL, }; // init static int __init sc1602_init(void) { int area, retval; volatile unsigned char *cscr = (volatile unsigned char *)CSCR; volatile unsigned char *wcrl = (volatile unsigned char *)WCRL; volatile unsigned char *wcrh = (volatile unsigned char *)WCRH; // デバイスドライバ登録 retval = register_chrdev(sc1602_major, sc1602_devname , &sc1602_fops); if (retval < 0) { printk(KERN_ERR "Cannnot register: %s(%d)\n", sc1602_devname, sc1602_major); return retval; } else { printk(KERN_INFO "%s(%d): registered.\n", sc1602_devname, sc1602_major ); printk(KERN_INFO " sc1602 command reg: 0x%x\n", SC1602_BASE ); printk(KERN_INFO " sc1602 data reg: 0x%x\n" , SC1602_BASE+SC1602_OFFS ); } // SC1602が割り当てられているエリア area = ((SC1602_BASE >> 21) & 7); // CSCRレジスタセット *cscr |= (1 << area) | 0x0F; // 3ウェイトステート挿入 if( area > 3 ) *wcrh |= (3 << (area-4)); else *wcrl |= (3 << area); // ほとんど不要だが… udelay( 15000 ); // wait > 15ms // SC1602初期化 // 最初の3つのファンクションはBFチェック不可 SC1602_CMD = 0x30; // ファンクションセット 8ビット udelay( 4500 ); // wait > 4.5ms SC1602_CMD = 0x30; // ファンクションセット 8ビット udelay(100); // wait > 100us SC1602_CMD = 0x30; // ファンクションセット 8ビット udelay(40); // wait > 40us // 2行表示 if( sc1602_writecmd( 0x38 ) < 0 ) return -EFAULT; // 表示オフ if( sc1602_writecmd( 0x08 ) < 0 ) return -EFAULT; // 表示オン、カーソルオフ、カーソルブリンクオフ if( sc1602_writecmd( 0x0C ) < 0 ) return -EFAULT; // エントリーモードセット=書き込み時アドレス増、シフトなし if( sc1602_writecmd( 0x06 ) < 0 ) return -EFAULT; // クリア if( sc1602_writecmd( 0x01 ) < 0 ) return -EFAULT; // wait > 1.65ms udelay( 1650 ); // Boot message sc1602_writechar( 'u' ); sc1602_writechar( 'C' ); sc1602_writechar( 'l' ); sc1602_writechar( 'i' ); sc1602_writechar( 'n' ); sc1602_writechar( 'u' ); sc1602_writechar( 'x' ); sc1602_writechar( '-' ); sc1602_writechar( 'H' ); sc1602_writechar( '8' ); return 0; } // exit static void __exit sc1602_cleanup(void) { // 特にすることはない printk( KERN_INFO "%s : removed.\n", msg ); } // モジュールの初期化、クリーンアップ関数登録 module_init(sc1602_init); module_exit(sc1602_cleanup);