• 검색 결과가 없습니다.

Text LCD 제어

문서에서 GettingStarted-with-HBE-EMPOSII (페이지 109-124)

8. 디바이스 드라이버

8.2. EMPOSⅡ에서 디바이스 드라이버 작성

8.2.3 Text LCD 제어

Text lcd 디바이스 드라이버를 만들기 전에 텍스트 lcd를 제어 할 수 있는 방법을 알아보 면 먼저 우리가 사용하는 Text lcd모듈은 8bit 마이크로프로세서가 내장되어있고 2개의 레 지스터가 존재하여 ARM에서 제어 할 수 있도록 되어 있다. 이 레지스터에 관하여 정확히 알면 손쉽게 프로그램을 할 수 있다. 아래는 이 레지스터들의 설명이다

- Text lcd의 레지스터들 Instruction

Register(IR)

Text lcd 모듈의 환경설정.

Data Register(DR) Text lcd 모듈에 글자를 표시하기 위한 데이터 값이

들어가는 레지스터.

- Text lcd 모듈의 내부 구조도

텍스트 lcd를 사용하기 위해서는 Instruction Register(IR)에 명령을 셋하고 Data

- 텍스트 LCD의 모듈의 제어 명령표

Set DD RAM Address 0 0 1 DD RAM Address 40uS

Read Busy Flag and

Address 0 1 BF Address Counter 40uS

Data Write to CG RAM or

DD RAM 1 0 Write Address 40uS

Data Read to CG RAM or

DD RAM 1 1 Read Address 40uS

텍스트 LCD 모듈의 제어 명령은 위와 같으며 이들 명령을 디바이스 드라이버에서 텍스트

3) Entry Mode Set

데이터를 read하거나 write할 경우에 커서의 위치를 증가(I/D=1)시킬 것 인가 감소 (I/D)시킬 것 인가를 결정하며, 또한 화면을 쉬프트 할 것인지(S=1) 아닌지(S=0)를 결정한다.

4) Display ON/OFF control

화면 표시를 ON/OFF 하거나(D), 커서를 ON/OFF 하거나(C), 커서를 깜빡이게 할 것 인가(B)의 여부를 지정한다.

5) Cursor or Display Shift

화면(S/C=1) 또는 커서(S/C=0)를 오른쪽(R/L=1) 또는 왼쪽(R/L=0)으로 쉬프트한다.

6) Function Set

인터페이스에서 데이터의 길이를 8비트(DL=1) 또는 4비트(DL=0)으로 지정하고, 화면 표시 행수를 2행(N=1) 또는 1행(N=0)으로 지정하며, 문자의 폰트를 5x10 dot(F=1) 또는 5x7 dot(F=0)로 지정한다.

7) Set CG(Character Generator) RAM Address

CG RAM의 어드레스를 지정한다. 이후에 송수신하는 데이터는 CG RAM 데이터이다.

8) Set DD(Display Data) RAM Address

DD RAM의 어드레스를 지정한다. 이후에 송수신하는 데이터는 DD RAM 데이터이다.

2) Display ON/OFF Control(0000 1xxx)을 보낸다.

3) Entry Mode Set(0000 01xx)을 보낸다.

4) DD RAM 주소를 보낸다.

5) 문자 데이터를 연속으로 보낸다.

위의 방식대로 텍스트 LCD를 제어하면 텍스트 LCD에 문자를 쓸 수 있으며 관련 디바이 스 드라이버는 다음과 같다.

- textlcdport.c

//fimename : textlcdport.c

#include <linux/module.h>

#include <asm/hardware.h>

#include <asm/uaccess.h>

#include <asm/ioctl.h>

#include <linux/ioport.h>

#include <asm/io.h>

#include <linux/delay.h>

#include "textlcdport.h"

void setcommand(unsigned short command){

command &= 0x00FF;

outl((command | 0x0000),TEXTLCDPORT_ADDRESS);

outl((command | 0x0400),TEXTLCDPORT_ADDRESS);

outl((command & 0x0000),TEXTLCDPORT_ADDRESS);

udelay(50);

void writebyte(char ch) {

unsigned short data;

data = ch & 0x00FF;

outl((data | 0x100),TEXTLCDPORT_ADDRESS);

outl((data | 0x500),TEXTLCDPORT_ADDRESS);

outl((data | 0x100),TEXTLCDPORT_ADDRESS);

udelay(50);

void initialize_textlcd(){

function_set(2,0); //Function Set:8bit,display 2lines,5x7 mod display_control(1,0,0); // Display on, Cursor off

clear_display(); // Display clear

entry_mode_set(1,0); // Entry Mode Set : shift right cursor return_home(); // go home

udelay(2000);

int i;

unsigned short command = 0x80;

unsigned short data;

command = row ? command + 0x40 : command;

setcommand(command);

for(i=0;i<length;i++) {

data = (*(str + i) & 0x00FF);

outl((data | 0x100),TEXTLCDPORT_ADDRESS);

outl((data | 0x500),TEXTLCDPORT_ADDRESS);

outl((data | 0x100),TEXTLCDPORT_ADDRESS);

udelay(50);

//send Function Set command to the text lcd // rows = 2 : => 2 rows, rows = 1 => 1 rows

// nfonts = 1 : = > 5x10 dot, nfonts = 0 : 5x7 dot int function_set(int rows, int nfonts){

unsigned short command = 0x30;

if(rows == 2) command |= 0x08;

else if(rows == 1) command &= 0xf7;

else return -1;

command = nfonts ? (command | 0x04) : command;

setcommand(command);

return 1;

command = cursor_enable ? (command | 0x02) : command;

command = nblink ? (command | 0x01) : command;

setcommand(command);

return 1;

int cusrsor_shit(int set_screen, int set_rightshit){

unsigned short command = 0x10;

command = set_screen ? (command | 0x08) : command;

command = set_rightshit ? (command | 0x04) : command;

setcommand(command);

return 1;

int entry_mode_set(int increase, int nshift){

unsigned short command = 0x04;

command = increase ? (command | 0x2) : command;

command = nshift ? ( command | 0x1) : command;

setcommand(command);

return 1;

int return_home(){

unsigned short command = 0x02;

setcommand(command);

udelay(2000);

return 1;

int clear_display(){

udelay(2000);

return 1;

int set_ddram_address(int pos){

unsigned short command = 0x80;

command += pos;

setcommand(command);

return 1;

// define functions...

static int textlcdport_open(struct inode *minode, struct file *mfile) { if(textlcdport_usage != 0) return -EBUSY;

MOD_INC_USE_COUNT;

textlcdport_usage = 1;

initialize_textlcd();

return 0;

static int textlcdport_release(struct inode *minode, struct file *mfile) {

MOD_DEC_USE_COUNT;

textlcdport_usage = 0;

return 0;

static ssize_t textlcdport_write(struct file *inode, const char *gdata, size_t length, loff_t *off_what) {

struct strcommand_varible strcommand;

copy_from_user(&strcommand,gdata,32);

write_string(1,strcommand.buf,strcommand.strlength);

return length;

static int textlcdport_ioctl(struct inode *inode, struct file

*file,unsigned int cmd,unsigned long gdata) { struct strcommand_varible strcommand;

copy_from_user(&strcommand,(char *)gdata,32);

break;

case TEXTLCD_DD_ADDRESS:

set_ddram_address((int)strcommand.pos);

break;

case TEXTLCD_WRITE_BYTE:

writebyte(strcommand.buf[0]);

break;

default:

printk("driver : no such command! n");

return -ENOTTY;

return 0;

static struct file_operations textlcd_fops = { write : textlcdport_write,

ioctl : textlcdport_ioctl, open : textlcdport_open, release : textlcdport_release,

;

int init_module(void){

int result;

result = register_chrdev(TEXTLCDPORT_MAJOR, TEXTLCDPORT_NAME,&textlcd_fops);

if(result < 0) {

printk(KERN_WARNING"Can't get any major n");

return result;

ADDRESS_RANGE,TEXTLCDPORT_NAME);

else

printk(KERN_WARNING"Can't get IO Region 0x%x n", TEXTLCDPORT_ADDRESS);

printk("init module, textlcdport major number : %d n",result);

return 0;

void cleanup_module(void) {

release_region(TEXTLCDPORT_ADDRESS, TEXTLCDPORT_ADDRESS_RANGE);

if(unregister_chrdev(textlcdport_major,TEXTLCDPORT_NAME)) printk(KERN_WARNING"%s DRIVER CLEANUP

FALLED n",TEXTLCDPORT_NAME);

- textlcdport.h

#define TEXTLCDPORT_MAJOR 0

#define TEXTLCDPORT_NAME "TEXT LCD PORT"

#define TEXTLCDPORT_MODULE_VERSION "TEXT LCD PORT V0.1"

#define TEXTLCDPORT_ADDRESS 0xf1700000

#define TEXTLCDPORT_ADDRESS_RANGE 2

#define TEXTLCDPORT_BASE 0xbc

#define TEXTLCD_COMMAND_SET _IOW(TEXTLCDPORT_BASE,0,32)

#defineTEXTLCD_FUNCTION_SET _IOW(TEXTLCDPORT_BASE,1,32)

#defineTEXTLCD_DISPLAY_CONTROL_IOW(TEXTLCDPORT_BASE,2,32)

#define TEXTLCD_CURSOR_SHIFT _IOW(TEXTLCDPORT_BASE,3,32)

#defineTEXTLCD_ENTRY_MODE_SET_IOW(TEXTLCDPORT_BASE,4,32)

#defineTEXTLCD_RETURN_HOME _IOW(TEXTLCDPORT_BASE,5,32)

#define TEXTLCD_CLEAR _IOW(TEXTLCDPORT_BASE,6,32)

#define TEXTLCD_DD_ADDRESS _IOW(TEXTLCDPORT_BASE,7,32)

#define TEXTLCD_WRITE_BYTE _IOW(TEXTLCDPORT_BASE,8,32)

//Global variable

static int textlcdport_usage = 0;

static int textlcdport_major = 0;

struct strcommand_varible { char rows;

void setcommand(unsigned short command);

void initialize_textlcd();

void write_string(int row, char *str, int length);

//void usr_wait(unsigned long delay_factor);

void setcommand(unsigned short command);

void writebyte(char ch);

void initialize_textlcd();

void write_string(int row, char *str,int length);

int function_set(int rows, int nfonts);

int display_control(int display_enable, int cursor_enable, int nblink);

int cusrsor_shit(int set_screen, int set_rightshit);

- test.c

#define TEXTLCDPORT_BASE 0xbc

#define TEXTLCD_COMMAND_SET _IOW(TEXTLCDPORT_BASE,0,32)

#define TEXTLCD_FUNCTION_SET _IOW(TEXTLCDPORT_BASE,1,32)

#defineTEXTLCD_DISPLAY_CONTROL_IOW(TEXTLCDPORT_BASE,2,32)

#define TEXTLCD_CURSOR_SHIFT _IOW(TEXTLCDPORT_BASE,3,32)

#defineTEXTLCD_ENTRY_MODE_SET_IOW(TEXTLCDPORT_BASE,4,32)

#define TEXTLCD_RETURN_HOME _IOW(TEXTLCDPORT_BASE,5,32)

#define TEXTLCD_CLEAR _IOW(TEXTLCDPORT_BASE,6,32)

#defineTEXTLCD_DD_ADDRESS _IOW(TEXTLCDPORT_BASE,7,32)

#defineTEXTLCD_WRITE_BYTE _IOW(TEXTLCDPORT_BASE,8,32)

struct strcommand_varible { char rows;

char buf[20];

;

int main(int argc, char **argv) {

int i,dev;

char buf[20] = "Hanback electronics";

char wellcom[20] = "Welcome to";

char wellcom2[20] = "the embeded World!";

struct strcommand_varible strcommand;

strcommand.rows = 0;

strcommand.nfonts = 0;

strcommand.display_enable = 1;

strcommand.cursor_enable = 0;

strcommand.nblink = 0;

strcommand.set_screen = 0;

strcommand.set_rightshit= 1;

strcommand.increase = 1;

strcommand.nshift = 0;

strcommand.pos = 10;

strcommand.command = 1;

strcommand.strlength = 18;

memcpy(&strcommand.buf[0],buf,20);

dev = open("/dev/textlcdport", O_WRONLY|O_NDELAY );

if (dev != -1) {

write(dev,&strcommand,32);

sleep(2);

memcpy(&strcommand.buf[0],&wellcom[i],1);

ioctl(dev,TEXTLCD_WRITE_BYTE,&strcommand,32);

sleep(2);

strcommand.pos = 40;

ioctl(dev,TEXTLCD_DD_ADDRESS,&strcommand,32);

for(i=0;i<18;i++) {

memcpy(&strcommand.buf[0],&wellcom2[i],20);

ioctl(dev,TEXTLCD_WRITE_BYTE,&strcommand,32);

close(dev);

else {

printf( "application : Device Open ERROR! n");

exit(-1);

return(0);

- Makefile

# Device Driver Makefile

CC = arm-linux-gcc

KERNELDIR = /usr/local/linux-2.4.19-rmk4-pax2-empx1 INCLUDEDIR = -I$(KERNELDIR)/include -I./

CFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 -I$(INCLUDEDIR)

MODULE_OBJS = textlcdport.o MODULE_SRCS = textlcdport.c

TEST_OBJS = test.o TEST_SRCS = test.c

all: $(MODULE_OBJS) $(TEST_TARGET)

$(MODULE_OBJS) :

$(CC) $(CFLAGS) -c $(MODULE_SRCS)

$(TEST_TARGET) :

$(CC) -o $(TEST_TARGET) $(TEST_SRCS)

clean:

rm -f *.o

rm -f $(TEST_TARGET)

소스 파일 작성이 완료되면 Makefile로 디바이스 드라이버와 응용 프로그램을 컴파일 하 고 시리얼 등을 통해 textlcdport.o와 test를 타겟 보드로 전송한다.

$ make

전송이 끝나면 insmod를 사용하여 디바이스 드라이버를 커널에 삽입하고 mknod로 특수 장치 파일을 만든다.

[root@EMPOS/root]$ insmod textlcdport.o

[root@EMPOS/root]$ mknod /dev/textlcdport c 253 0

이상으로 응용프로그램의 실행 준비가 끝나면 응용프로그램을 다음과 같이 실행시켜 디바 이스를 테스트 할 수 있다.

[root@EMPOS/root]$ ./test

위 명령을 통해 LCD의 첫 번째 줄에 “Hanback electronics”라는 문장이 써지고 얼마 후 에 “Welcome to the embedded World!”라는 문장이 첫 번째 줄과 두 번째 줄에 나타나게 된다.

※ 동작 원리

Textlcd 디바이스 드라이버에서는 setcommand()와 writebyte()를 사용하여 텍스트 LCD의 Write 타이밍을 맞추고 이것을 이용하여 텍스트 LCD 모듈의 제어 명령에 따른 각각의 함

적을 수 있는 버퍼가 있어 이 곳에 사용할 명령 코드나 텍스트를 넣어 사용자 어플리케이 struct timer_list {

struct list_head list;

unsigned long expires;

unsigned long data;

void (*function)(unsigned long);

;

function필드는 타이머가 만료될 때 실행되는 함수 주소이고 data필드는 이 타이머 함수 에 전달할 매개 변수를 지정하며 expires필드는 타이머가 만료될 시간을 지정한다. list필 드는 이중 원형 연결 리스트를 구현하는 데 사용된다.

드라이버에서 타이머를 만들고 이를 활성화하는 방법은 다음과 같다.

1. 새로운 struct timer_list(mytimer) 객체를 만든다.

2. init_timer(&mytimer)를 호출해서 객체를 초기화한다.

3. expires필드에 해당 값을 지정한다.

4. 타이머가 만료될 때 function필드를 호출할 함수 주소를 설정한다.

5. add_timer(&mytimer)를 호출해서 mytimer항목을 리스트에 삽입한다.

6. 마지막으로 타이머를 다 사용했으면 del_timer()를 호출하여 타이머 리스트에서 제거한

문서에서 GettingStarted-with-HBE-EMPOSII (페이지 109-124)