星期六, 5月 12, 2007

單色 LCD 驅動程式

V1.1

copyright@2006

email: mesmerli@hotmail.com

LCD 驅動程式實驗步驟

Host 端

  1. root file system 組態設定。
    1. 建構新的 root file system (RAMDISK)
      1. dd if=/dev/zero of=ext2new bs=1k count=8192
      2. mke2fs -F -m0 -I 2000 ext2new
      3. mount -w -o loop ext2new /mnt/loop
      4. mount -w -o loop ext2org /mnt/looporg
      5. cp -dpR /mnt/looporg/. /mnt/loop/.
      6. cd /mnt/loop/dev (/dev 檔案夾下,建立 裝置節點)
      7. mknod -m 777 lcd0 c 40 0
      8. cd /
      9. umount -l /mnt/loop
      10. gzip -9 ext2new
    2. 重新燒錄 root file system。
  2. 建構 LED 驅動程式
    1. 進入 lcd-demo 目錄
    2. make

Taget 端

  1. 使用 NFS 的方式,mount host 端的目錄,以存取剛剛建立的 LED 驅動程式。
    1. mount 192.168.0.200:/usr/src/creator/nfs /mnt

  1. 安裝驅動程式

    1. cd /dev
    2. mknod -m 777 lcd0 c 40 0
    3. cd /mnt/Day3/char-driver/lcd-demo
    4. insmod lcd-creator.o
  2. 執行應用程式
    1. ./lcd-demo.exe

lcd-creator.c

// --------------------------------------------------------------------
//
// Title : lcd-creator.c
// :
// Library :
// :
// Developers: MICROTIME MDS group (V1.0)
// : mesmerli@gmail.com (V1.1)
// :
// Purpose : Driver for AF-128128CFI-H LCD(128*128 pixels, 4 colors)
// :
// Limitation:
// :
// Note :
// :
// --------------------------------------------------------------------
// modification history :
// --------------------------------------------------------------------
// Version| mod. date: |
// V1.0 | 03/05/2004 | First release
// V1.1 | 11/15/2004 | LCD Only by mesmerli
// --------------------------------------------------------------------
//
// Note:
//
// MICROTIME COMPUTER INC.
//
//

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <asm/irq.h>
#include <asm/param.h>
#include <asm/uaccess.h>
#include "asm/arch/irqs.h"

#if LINUX_VERSION_CODE < 0x020100
#define GET_USER(a,b) a = get_user(b)
#else
#include <asm/uaccess.h>
#define GET_USER(a,b) get_user(a,b)
#endif

#include "asm/arch/lib/creator_s3c2410_addr.h"
#include "asm/arch/lib/genfont8_8.h"

#include "lcd-creator.h"

/*
* Define driver major number.
*/
#define MAJOR_NUM LCD_ONLY_MAJOR_NUM
#define MODULE_VERSION "1.10"
#define MODULE_NAME "LCD_CREATOR"
#define COPYRIGHT "Copyright (C) 2003-2004, Microtime Computer Inc."
#define MODULE_AUTHOR_STRING "Microtime Computer Inc."
#define MODULE_DESCRIPTION_STRING "Creator LCD module"

enum LCDCommand{
LCD_START_OSC=0, // Start oscillation
LCD_DEVICE_CODE=0, // Device code read
LCD_DRV_OUT=1, // Driver output control
LCD_WAVEFORM=2, // LCD-driving-waveform control
LCD_PWR_CONTROL=3, // Power control
LCD_CONTRAST=4, // Contrast control
LCD_ENTRY_MODE=5, // Entry mode
LCD_ROTATION=6, // Rotation
LCD_DISPLAY=7, // Display control
LCD_CURSOR=8, // Cursor control
LCD_DB_HEIGHT=9, // Double-height display position
LCD_VET_SCROLL=0xa, // Vertical scroll
LCD_HOR_CURSOR_POS=0xb, // Horizontal cursor position
LCD_VER_CURSOR_POS=0xc, // Vertical cursor position
LCD_RAM_MASK=0x10, // RAM write data mask
LCD_RAM_ADDR=0x11, // RAM address set
LCD_RAM_DATA=0x12, // RAM Data
LCD_RAM_WR=0x12, // RAM data write
LCD_RAM_RD=0x12 // RAM data read
};


// 讀寫LCD RAM的Macro
#define SET_ADDRESS_LCD(x) WriteLCD(LCD_RAM_ADDR, x);
#define SET_RAM_DATA_LCD(x) WriteLCD(LCD_RAM_DATA, x);

static unsigned short ReadLCD (unsigned short addr);
static void WriteLCD (unsigned short addr, unsigned short data);
static void LCD_ClearViewArea (void);
static void LCD_PutRAMData (unsigned short address, unsigned short data);
static void LCD_ClearScreen (void);
static void LCD_ClearOneLine (int y);
static void LCD_scroll (void);
static void LCD_displ (int x, int y, int ch);
static void LCD_putchar (char c);
static void LCD_CursorMode (unsigned char Mode);
static void LCD_EnableCursor (int bCursorOn);
static void LCD_SetCursor(int x, int y);
static void InitialLCD (void);
static void Initial_LCD (void);

static void OptimizationDelay(void) {} // for Optimization

static unsigned char Xpos, Ypos, ScrWidth, ScrHeight,YScrollOffset;
static unsigned char byFontHeight, byFontWidth;
static unsigned char byCursorOn, byCursorMode;

//對LCD讀取資料
static unsigned short ReadLCD (unsigned short addr)
{
unsigned short data;

LCD_CMD = 0xff; //MSB
OptimizationDelay(); //for Optimization
LCD_CMD = addr; //LSB
data = LCD_DATA << 8; //MSB
data |= LCD_DATA; //LSB

// 若是讀取LCD的顯示區域CGRAM資料時,
// 第一次資料是不合法, 第二次才是正確資料
if (addr == LCD_RAM_DATA) {
LCD_CMD = 0xff; //MSB
OptimizationDelay(); //for Optimization
LCD_CMD = addr; //LSB
data = LCD_DATA << 8; //MSB
data |= LCD_DATA; //LSB
}
return (data);
}

//對LCD設定資料
static void WriteLCD (unsigned short addr, unsigned short data)
{
LCD_CMD = 0xff; //MSB
OptimizationDelay(); //for Optimization
LCD_CMD = addr; //LSB

LCD_DATA = (data >> 8) ; //MSB
OptimizationDelay(); //for Optimization
LCD_DATA = data; //LSB
}

//清除LCD畫面, 並重新設定垂直捲動
static void LCD_ClearViewArea (void)
{
int i;

SET_ADDRESS_LCD(0); //設定開始寫入的LCD RAM的起始位址
for (i=0; i < 0x800; i++) //清除LCD畫面
SET_RAM_DATA_LCD(0);

WriteLCD(LCD_VET_SCROLL, 0); //重新設定垂直捲動
}

//將資料寫入LCD的顯示區域內
static void LCD_PutRAMData (unsigned short address, unsigned short data)
{
SET_ADDRESS_LCD(address); //設定LCD顯示區域RAM的位址
SET_RAM_DATA_LCD(data); //將所指定的資料寫入所指定LCD顯示區域RAM
}

//清除整個LCD營幕成空白, 並且游標重新歸位為原點
static void LCD_ClearScreen (void)
{
LCD_ClearViewArea();
Xpos = Ypos = YScrollOffset = 0;
WriteLCD(LCD_VET_SCROLL, 0);
LCD_SetCursor(Xpos, Ypos);
}

//將LCD某行變成空白行
static void LCD_ClearOneLine (int y)
{
int x;

for (x=0; x < ScrWidth; x++)
LCD_displ(x, y, ' '); //在X,Y寫入空白字元
Xpos = 0;
}

//LCD往上垂直捲動一行
static void LCD_scroll (void)
{
if (YScrollOffset == 0)
YScrollOffset = ScrHeight - 1;
else
YScrollOffset--;

WriteLCD(LCD_VET_SCROLL, byFontHeight * (Ypos - YScrollOffset + 1));
LCD_ClearOneLine(Ypos); //最下面一行清除為空白行
}

//將單一個字元寫入LCD
static void LCD_displ (int x, int y, int ch)
{
unsigned short addr, data ;
int i, YRelation;

//可顯示自元範圍從0x20~0x7E
//其餘用空白取代
data = (ch >= 0x20 && ch <= 0x7E) ? ch : ' ';

//計算字元所在位置
YRelation = y - YScrollOffset;
addr = x + (YRelation * 0x80);

//取出字元的圖型資料後寫入LCD
for (i=0; i < byFontWidth; i++){
LCD_PutRAMData(addr, charset[data][i]);
addr += 0x10;
}
}

//將單一個字元寫入顯示裝置
static void LCD_putchar (char c)
{
switch (c){
case 'n' : //換行字元
if (Ypos == (ScrHeight-1)) //如果到達最後一行將往上捲動一行
LCD_scroll();
else
Ypos++;
Xpos = 0;
break ;
case 't' : //TAB字元用空白字元替換
do LCD_putchar(' ');
while (Xpos%TABS);
break ;
default : //當超過每行最大寬度時自動換行
if ((Xpos+1) >= ScrWidth){
LCD_putchar('n');
}
LCD_displ(Xpos++, Ypos, c); //將字元寫入LCD
break ;
}
if (byCursorOn)
LCD_SetCursor(Xpos, Ypos); //移動游標到所顯式的字元後面
}

//設定游標模式
static void LCD_CursorMode (unsigned char Mode)
{
byCursorMode = Mode & 0x3;
}

//啟動/關閉游標
static void LCD_EnableCursor (int bCursorOn)
{
unsigned short data;

byCursorOn = bCursorOn;
if (byCursorOn)
data = byCursorMode | 0x04;
else
data = 0;
WriteLCD(LCD_CURSOR, data);
}

//讀取游標位置
static void LCD_GetCursor(int *x, int *y)
{
*x = Xpos ; *y = Ypos;
}

//設定游標位置
static void LCD_SetCursor(int x, int y)
{
unsigned short HS, HE, VS;

HS = x * byFontWidth;
HE = HS + (byFontWidth - 1);
WriteLCD(LCD_HOR_CURSOR_POS, (HE << 8) + HS);

VS = (y + 1) * byFontHeight - 2;
WriteLCD(LCD_VER_CURSOR_POS, ((VS+1)<<8) + VS);

Xpos = x; Ypos = y;
}


static void
LCD_DrawFullImage (unsigned short *pImageBuffer)
{
int i;

SET_ADDRESS_LCD(0);

for (i=0; i < 0x800; i++){
SET_RAM_DATA_LCD(pImageBuffer[i]);
}
}

// 初始化LCD的命令集
static void InitialLCD (void)
{
int i;

//LCD初始命令同步
for (i=0; i < 4; i++){
LCD_CMD = 0; //同步命令
OptimizationDelay();
}

WriteLCD(LCD_START_OSC, 1); //重新啟動LCD
mdelay(15); //延遲等待LCD進入穩定工作狀態

//128 * 128 ; Inervt COM & SEG
//CMS=1, SGS=1, CN=0, NL3-0=1111:128*128 dots,
WriteLCD(LCD_DRV_OUT, 0x030f);

//B/C=0, EOR=0, NW4-0=0000
WriteLCD(LCD_WAVEFORM, 0);

//BS2-0=000:1/11 bias driver, BT1-0=10:Six-times boost
//DC1-0=11:4-divided clock, AP1-0=11:Large,
//SLP=0, Sleep mode off, STB=0:Standby mode off)
WriteLCD(LCD_PWR_CONTROL, 0x23c);

//CT5-0=011000:
WriteLCD(LCD_CONTRAST, 0x18);

//I/D=1:Increment by 1, AM1-0=00:Horizontal move, LG1=00:Replace mode)
WriteLCD(LCD_ENTRY_MODE, 0x10);

//RT2-0=000:No shift)
WriteLCD(LCD_ROTATION, 0);

//C=1:Cursor display started, CM1-0=00:White-blink cursor)
WriteLCD(LCD_CURSOR, 4);

//DE6-0=0000000:, DS6-0=0000000:
WriteLCD(LCD_DB_HEIGHT, 0);

//SL6-0=0000000:First raster-row displayed at the top
WriteLCD(LCD_VET_SCROLL, 0);

//HE6-0=HS6-0=VE6-0=VS6-0=0000000
WriteLCD(LCD_HOR_CURSOR_POS, 0);

//VM15-0=0000H:No mask
WriteLCD(LCD_RAM_MASK, 0);

//AD10-0=000H
WriteLCD(LCD_RAM_ADDR, 0);

//PS1-0=00:Partial scroll off, DHE=0:Double-height display off,
//GS=0, REV=0, D=1:Dispkay on
WriteLCD(LCD_DISPLAY, 1);

LCD_ClearScreen(); //清除畫面成空白
}

// 規畫LCD操作模式
// 總共多少行, 每行多少字
// 設定游標型態
static void Initial_LCD (void)
{
byFontHeight = 8; //文字字型高度為8
byFontWidth = 8; //文字字型寬度為8
ScrWidth = 128 / byFontWidth; //LCD每行共多少文字
ScrHeight = 128 / byFontHeight; //LCD總共有多少行

InitialLCD(); //LCD工作模式設定

LCD_CursorMode(BLACK_BLINK_CURSOR); //黑色閃爍的游標
LCD_EnableCursor(UM); //啟動游標

}

static int drv_lcdtxt_open(struct inode *inode, struct file *filp)
{
MOD_INC_USE_COUNT;
return(0);
}
/*****************************************************************************/


static int drv_lcdtxt_release(struct inode *inode, struct file *filp)
{
MOD_DEC_USE_COUNT;
return 0;
}
/*****************************************************************************/

static ssize_t drv_lcdtxt_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
{
char *dp, c;
int num;

dp = (char *) buf;

for (num = 0; (num < count); num++) {
GET_USER(c, dp++);
LCD_putchar(c);
}

return(num);
}

int drv_lcdtxt_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
lcd_write_info_t display;
int rc = 0;

/*
分離type如果遇到錯誤的cmd, 就直接傳回ENOTTY
*/
if (_IOC_TYPE(cmd) != LCD_IOCTL_MAGIC) return (-ENOTTY);

switch (cmd) {
case LCD_IOCTL_CLEAR : /* 清除整個螢幕 */
LCD_ClearScreen();
break;

case LCD_IOCTL_WRITE : {
int index;

if (copy_from_user(&display, (lcd_write_info_t*)arg, sizeof(lcd_write_info_t)))
return (-EINVAL);
for (index = 0; index < (display.Count); index++){
LCD_putchar(display.Msg[index]);
}
break ;
}
case LCD_IOCTL_CUR_ON :
LCD_EnableCursor(OK);
break ;
case LCD_IOCTL_CUR_OFF :
LCD_EnableCursor(OFF);
break ;
case LCD_IOCTL_CUR_GET :
if (copy_from_user(&display, (lcd_write_info_t*)arg, sizeof(lcd_write_info_t)))
return (-EINVAL);
LCD_GetCursor(&display.CursorX, &display.CursorY);
if (copy_to_user((unsigned short*)arg, &display, sizeof(lcd_write_info_t)))
return (-EINVAL);
break ;

case LCD_IOCTL_CUR_SET :
if (copy_from_user(&display, (lcd_write_info_t*)arg, sizeof(lcd_write_info_t)))
return (-EINVAL);
LCD_SetCursor(display.CursorX, display.CursorY);
break ;

case LCD_IOCTL_DRAW_FULL_IMAGE :{
lcd_full_image_info_t image;
if (copy_from_user(&image, (lcd_full_image_info_t*)arg, sizeof(lcd_full_image_info_t)))
return (-EINVAL);
LCD_DrawFullImage(image.data);
break;
}
default:
rc = -ENOTTY;
break;
}

return(rc);
}


/*****************************************************************************/

/*
* Exported file operations structure for driver...
*/

struct file_operations drv_lcdtxt_fops =
{
write: drv_lcdtxt_write,
ioctl: drv_lcdtxt_ioctl,
open: drv_lcdtxt_open,
release: drv_lcdtxt_release,
};

/*****************************************************************************/
static int __init init_module_drv_lcdtxt(void)
{
int rc;

SET_MODULE_OWNER(&drv_lcdtxt_fops);

/* Register lcdtxt as character device */
if ((rc = register_chrdev(MAJOR_NUM, MODULE_NAME, &drv_lcdtxt_fops)) < 0) {
printk("<1>%s: can't get major %dn", MODULE_NAME, MAJOR_NUM);

return (-EBUSY);
}
printk("<1>%s: Version : %s %sn", MODULE_NAME, MODULE_VERSION, COPYRIGHT);

/* Hardware specific initialization */
Initial_LCD();
LCD_EnableCursor(OK);

return 0;
}

static void __exit cleanup_module_drv_lcdtxt(void)
{
unregister_chrdev(MAJOR_NUM, MODULE_NAME);
printk("<1>%s: removedn", MODULE_NAME);
}


/* here are the compiler macro for module operation */
module_init(init_module_drv_lcdtxt);
module_exit(cleanup_module_drv_lcdtxt);

MODULE_AUTHOR(MODULE_AUTHOR_STRING);
MODULE_DESCRIPTION(MODULE_DESCRIPTION_STRING);

EXPORT_NO_SYMBOLS;

lcd-creator.h

//=============================================================================
// File Name : lcd-creator.h
// Function : LCD device drvier definition
// Program :
// Date : 11/15/2004
// Version : 1.10
// History
// 1.0.0 : Programming start (03/05/2004) -> SOP
// 1.1.0 : LCD only version
//=============================================================================

lcd-demo.c

#ifndef _LCD_CREATOR_H_
#define _LCD_CREATOR_H_

#include <linux/config.h>
#if defined(__linux__)
#include <asm/ioctl.h> /* For _IO* macros */
#define LCD_IOCTL_NR(n) _IOC_NR(n)
#elif defined(__FreeBSD__)
#include <sys/ioccom.h>
#define LCD_IOCTL_NR(n) ((n) & 0xff)
#endif

#define LCD_ONLY_MAJOR_NUM 40
#define LCD_IOCTL_MAGIC LCD_ONLY_MAJOR_NUM
#define LCD_IO(nr) _IO(LCD_IOCTL_MAGIC,nr)
#define LCD_IOR(nr,size) _IOR(LCD_IOCTL_MAGIC,nr,size)
#define LCD_IOW(nr,size) _IOW(LCD_IOCTL_MAGIC,nr,size)
#define LCD_IOWR(nr,size) _IOWR(LCD_IOCTL_MAGIC,nr,size)

// function headers

typedef struct lcd_write_info { /* for LCD */
unsigned char Msg[512] ; /* 輸入的字元存放區,最大512 */
unsigned short Count; /* 實際輸入的字元長度 */
int CursorX, CursorY;/* 讀取 cursor 設定游標之X, Y */
} lcd_write_info_t ;

typedef struct lcd_full_image_info {
unsigned short data[0x800]; /* LCD整個資料 */
} lcd_full_image_info_t;

// 游標各種操作模式
#define WHITE_BLINK_CURSOR 0 // 白色閃爍游標
#define BLACK_BLINK_CURSOR 1 // 黑色閃爍游標
#define REVERSE_CURSOR 2 // 黑白交換的游標
#define REVERSE_BLINK_CURSOR 3 // 黑白閃爍交換的游標

#define TABS 4 // 多少空白字代表TAB

/* LCD specific ioctls */
/* 清除LCD上的資料並且游標回到左上角 */
#define LCD_IOCTL_CLEAR LCD_IO( 0x0)
/* 寫字元到LCD 上*/
#define LCD_IOCTL_WRITE LCD_IOW( 0x01, lcd_write_info_t)
#define LCD_IOCTL_CUR_ON LCD_IO( 0x02)
#define LCD_IOCTL_CUR_OFF LCD_IO( 0x03)
#define LCD_IOCTL_CUR_GET LCD_IOR( 0x04, lcd_write_info_t)
#define LCD_IOCTL_CUR_SET LCD_IOW( 0x05, lcd_write_info_t)
#define LCD_IOCTL_DRAW_FULL_IMAGE LCD_IOW( 0x06, lcd_full_image_info_t)


#endif // _LCD_CREATOR_H_

lcd-demo.c

#include <signal.h>
#include <stdio.h>
#include <strings.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ioctl.h>

#include "lcd-creator.h"

int main()
{
int fd;
lcd_write_info_t display;
int ret;

fd = open("/dev/lcd0", O_RDWR);
if (fd < 0)
{
printf("open /dev/lcd0 errorn");
return (-1);
}

display.Count = sprintf(display.Msg, "Hello World!");
ret = ioctl(fd, LCD_IOCTL_WRITE, &display);
// display.DB_Row=0;
// ret = ioctl(fd,LCD_IOCTL_DB_HEIGHT,&display);

printf("Hello World!!!n");

return 0;
}

0 意見: