TextLCD 디바이스
TextLCD 디바이스 TextLCD HBE-SM III-SV210의 TextLCD 영문자와 숫자 일본어, 특수문자를 표현하여 정보를 전달하는데 사용되는 디바이스이다. HBE-SM III-SV210의 TextLCD 16문자*2라인을 Display TextLCD를 제어하기 위하여 FPGA 내부에 Controller가 설계되어 있다
TextLCD 디바이스 TextLCD Control Register의 데이터 비트 구조를 나타낸 표 Physical Address 0x8800_0010 CLCD_Ctrl_Reg BIT 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 Reserved CLCD_RS CLCD_RW CLCD_E CLCD_D7 CLCD_D6 CLCD_D5 CLCD_D4 CLCD_D3 CLCD_D2 CLCD_D1 CLCD_D0 Reset X Bits Name Description CLCD Data Bit 0 CLCD Data Bit 1 CLCD Data Bit 2 CLCD Data Bit 3 CLCD Data Bit 4 CLCD Data Bit 5 CLCD Data Bit 6 CLCD Data Bit 7 CLCD Enable Bit (Active High) CLCD Read/Write Control Bit (‘1’read, ‘0’Write) CLCD Data Instruction Register Set
TextLCD 디바이스 TextLCD 모듈의 제어 명령표 기 능 제어 신호 제어 명령 (D0 ~ D7) 실행 시간 RS R/W 6 5 4 3 2 1 Clear Display 1.64ms Return Home 40us Entry Mode Set I/D S Display on/off control D C Cursor or Display Shift S/C R/L Function Set D/L N F Set CG RAM Address CG RAM Address Set DD RAM Address DD RAM Address Read Busy Flag and Address BF Address Counter Data Write to CG RAM or DD RAM Write Address Data Read to CG RAM or DD RAM Read Address
디바이스 드라이버 textlcd.ko
Devide Driver textlcd textlcd.c modules.order Makefile lcdtest.c – LCD 테스트 어플리케이션
Makefile # # Makefile for TextLCD Device Driver # Hanback Electronics Co.,ltd CC = arm-linux-gcc obj-m := textlcd.o KDIR := /Android/linux-2.6.35-hanback PWD := $(shell pwd) TEST_TARGET = lcdtest TEST_SRCS = lcdtest.c default:$(TEST_TARGET) $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules $(CC) -o $(TEST_TARGET) $(TEST_SRCS) rm -f default clean: rm -f *.ko rm -f *.o rm -f *.mod.* rm -f .*. rm -f $(TEST_TARGET)
textlcd.c(#1) /* * TextLCD Device Driver * Hanback Electronics Co.,ltd * File : textlcd.c * Date : April,2009 */ // 모듈의 헤더파일 선언 #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/ioport.h> #include <asm/io.h> #include <asm/ioctl.h> #include <asm/uaccess.h> #include <linux/delay.h>
textlcd.c(#2) #include "textlcd.h" #define SET_1 1 #define SET_2 1 #define SET_3 1 #define WRI_1 1 #define WRI_2 1 #define WRI_3 1 // 디바이스 드라이버 내부에서 사용하는 함수 // 이 함수에 명령값 command를 인자로 실행하면 TextLCD에 명령이 실행된다. void setcommand(unsigned short command) { command &= 0x00FF; *textlcd_ioremap = command | 0x0000; mdelay(1); *textlcd_ioremap = command | 0x0100; }
textlcd.c(#3) // TextLCD에 문자를 출력하는 함수 // ch에 표시하고자 하는 문자값을 입력해서 이 함수를 실행하면 TextLCD에 나타난다. void writebyte(char ch) { unsigned short data; data = ch & 0x00FF; *textlcd_ioremap = data & 0x400; mdelay(1); *textlcd_ioremap = data | 0x500; *textlcd_ioremap = data | 0x400; } // TextLCD를 초기화 하는 함수 void initialize_textlcd() function_set(2,0); //Function Set:8bit,display 2l ines,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
// TextLCD를 두 줄로 표시할지와 폰트 크기를 정하는 함수 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; } // TextLCD의 표시방법과 커서를 보이게 할 것인지에 대한 설정함수 int display_control(int display_enable, int cursor_enable, int nblink) unsigned short command = 0x08; command = display_enable ? (command | 0x04) : command; command = cursor_enable ? (command | 0x02) : command; command = nblink ? (command | 0x01) : command;
// 표시된 문자들을 쉬프트하는 함수 int cursor_shift(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; } // TextLCD의 모드를 지정하는 함수 int entry_mode_set(int increase, int nshift) unsigned short command = 0x04; command = increase ? (command | 0x2) : command; command = nshift ? ( command | 0x1) : command;
// TextLCD의 커서를 홈으로 옮기는 함수 int return_home() { unsigned short command = 0x02; setcommand(command); return 1; } // TextLCD의 화면을 지우는 함수 int clear_display() unsigned short command = 0x01; // TextLCD의 ddram의 주소를 설정하는 함수 int set_ddram_address(int pos) unsigned short command = 0x80; command += pos;
// open() 함수를 이용하여 디바이스 드라이버가 열린 경우 호출되는 함수 static int textlcd_open(struct inode *minode, struct file *mfile) { // 디바이스가 열려 있는지 확인. if(textlcd_usage != 0) return -EBUSY; // 가상주소 매핑 textlcd_ioremap= (unsigned short *)ioremap(TEXTLCD_ADDRESS,TEXTLCD_ADDRESS_RANGE); // 등록할 수 있는 I/O 영역인지 확인 if(!check_mem_region((unsigned long)textlcd_ioremap,TEXTLCD_ADDRESS_RANGE)) // I/O 메모리 영역을 등록 request_mem_region((unsigned long)textlcd_ioremap,TEXTLCD_ADDRESS_RANGE,TEXTLCD_NAME); } else printk(KERN_WARNING"Can't get IO Region 0x%x\n",TEXTLCD_ADDRESS); textlcd_usage = 1; initialize_textlcd(); return 0;
// 응용 프로그램에서 디바이스를 더이상 사용하지 않아서 닫기를 구현하는 함수 static int textlcd_release(struct inode *minode, struct file *mfile) { // 매핑된 가상주소를 해제 iounmap(textlcd_ioremap); // 등록된 I/O 메모리 영역을 해제 release_mem_region((unsigned long)textlcd_ioremap,TEXTLCD_ADDRESS_RANGE); textlcd_usage = 0; return 0; }
// 디바이스 드라이버의 쓰기를 구현하는 함수 static ssize_t textlcd_write(struct file // 디바이스 드라이버의 쓰기를 구현하는 함수 static ssize_t textlcd_write(struct file *inode, const char*gdata, size_t length, loff_t *off_what) { int i,ret; char buf[100]; // 사용자 메모리 gdata를 커널 메모리 buf에 length만큼 복사 ret=copy_from_user(buf,gdata,length); if(ret < 0) return -1; for(i=0;i<length;i++) { writebyte(buf[i]); } return length;
//read()와 write()로 구현하기 곤란한 디바이스 드라이버의 입출력 처리를 구현하는 함수 static int textlcd_ioctl(struct inode *inode, struct file *file,unsigned int cmd,unsigned long gdata) { struct strcommand_varible strcommand; int ret; // 사용자 메모리 gdata를 커널 메모리 strcommand로 32바이트 복사 ret=copy_from_user(&strcommand,(char *)gdata,32); if(ret<0) return -1; switch(cmd){ case TEXTLCD_COMMAND_SET: setcommand(strcommand.command); break; case TEXTLCD_FUNCTION_SET: function_set((int)(strcommand.rows+1),(int)(strcommand.nfonts)); case TEXTLCD_DISPLAY_CONTROL: display_control((int)strcommand.display_enable, (int)strcommand.cursor_enable,(int)strcommand.nblink);
case TEXTLCD_CURSOR_SHIFT: cursor_shift((int)strcommand case TEXTLCD_CURSOR_SHIFT: cursor_shift((int)strcommand.set_screen,(int)strcommand.set_rightshit); break; case TEXTLCD_ENTRY_MODE_SET: entry_mode_set((int)strcommand.increase,(int)strcommand.nshift); case TEXTLCD_RETURN_HOME: return_home(); case TEXTLCD_CLEAR: clear_display(); case TEXTLCD_DD_ADDRESS: set_ddram_address((int)strcommand.pos); case TEXTLCD_WRITE_BYTE: writebyte(strcommand.buf[0]); default: printk("driver : no such command!\n"); return -ENOTTY; } return 0;
// 파일 오퍼레이션 구조체 // 파일을 열때 open()을 사용한다. open()는 시스템 콜을 호출하여 커널 내부로 들어간다. // 해당 시스템 콜과 관련된 파일 연산자 구조체 내부의 open에 해당하는 필드가 드라이버 내에서 // textlcd_open()으로 정의되어 있으므로 textlcd_open()가 호출된다. // 만약 등록되지 않은 동작에 대해서는 커널에서 정의해 놓은 default 동작을 하도록 되어 있다. static struct file_operations textlcd_fops = { .owner = THIS_MODULE, .open = textlcd_open, .write = textlcd_write, .ioctl = textlcd_ioctl, .release = textlcd_release, };
// 모듈을 커널 내부로 삽입 // 모듈 프로그램의 핵심적인 목적은 커널 내부로 들어가서 서비스를 제공받는 것이므로 // 커널 내부로 들어가는 init()을 먼저 시작한다. // 응용 프로그램은 소스 내부에서 정의되지 않은 많은 함수를 사용한다. 그것은 외부 // 라이브러리가 컴파일 과정에서 링크되어 사용되기 때문이다. 모듈 프로그램은 커널 // 내부하고만 링크되기 때문에 커널에서 정의하고 허용하는 함수만을 사용할 수 있다. int textlcd_init(void) { int result; // 문자 디바이스 드라이버를 등록한다. result = register_chrdev(TEXTLCD_MAJOR,TEXTLCD_NAME,&textlcd_fops); if(result < 0) { printk(KERN_WARNING"Can't get any major\n"); return result; } // major 번호를 출력한다. printk(KERN_INFO"Init Module, TextLCD Major Number : %d\n",TEXTLCD_MAJOR); return 0;
// 모듈을 커널에서 제거 void textlcd_exit(void) { // 문자 디바이스 드라이버를 제거한다 // 모듈을 커널에서 제거 void textlcd_exit(void) { // 문자 디바이스 드라이버를 제거한다. unregister_chrdev(TEXTLCD_MAJOR,TEXTLCD_NAME); printk("driver: %s DRIVER EXIT\n", TEXTLCD_NAME); } module_init(textlcd_init); // 모듈 적재 시 호출되는 함수 module_exit(textlcd_exit); // 모듈 제거 시 호출되는 함수 MODULE_AUTHOR(DRIVER_AUTHOR); // 모듈의 저작자 MODULE_DESCRIPTION(DRIVER_DESC);// 모듈에 대한 설명 MODULE_LICENSE("Dual BSD/GPL"); // 모듈의 라이선스 등록
module.order kernel//Android/device_drivers/243.textlcd_driver/textlcd.ko
Application(lcdtest .c) #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #define TEXTLCD_BASE 0xbc #define TEXTLCD_COMMAND_SET _IOW(TEXTLCD_BASE,0,32) #define TEXTLCD_FUNCTION_SET _IOW(TEXTLCD_BASE,1,32) #define TEXTLCD_DISPLAY_CONTROL _IOW(TEXTLCD_BASE,2,32) #define TEXTLCD_CURSOR_SHIFT _IOW(TEXTLCD_BASE,3,32) #define TEXTLCD_ENTRY_MODE_SET _IOW(TEXTLCD_BASE,4,32) #define TEXTLCD_RETURN_HOME _IOW(TEXTLCD_BASE,5,32) #define TEXTLCD_CLEAR _IOW(TEXTLCD_BASE,6,32) #define TEXTLCD_DD_ADDRESS _IOW(TEXTLCD_BASE,7,32) #define TEXTLCD_WRITE_BYTE _IOW(TEXTLCD_BASE,8,32)
struct strcommand_varible { char rows; char nfonts; char display_enable; char cursor_enable; char nblink; char set_screen; char set_rightshit; char increase; char nshift; char pos; char command; char strlength; char buf[20]; };
int main(int argc, char **argv) { int dev; struct strcommand_varible strcommand; strcommand.pos = 0x40; dev = open("/dev/textlcd", O_WRONLY|O_NDELAY ); if (dev != -1) write(dev,"Welcome !",15); ioctl(dev,TEXTLCD_DD_ADDRESS,&strcommand,32); write(dev,"Hello Embedded! ",15); close(dev); } else { printf( "application : Device Open ERROR!\n"); exit(-1); return(0);
컴파일(Compile) make 우분투 터미널 창에서 경로 : /Android/device_drivers/243.textlcd_driver# 안에서 make
임베디드 보드로 옮기기 [STEP] #cd /Android/driver/243.textlcd_driver [STEP1] #cp textlcd.ko /tftpboot/ [STEP2] #cp lcdtest /tftpboot/ [STEP3] 보드 전원과 시리얼 케이블 연결확인 !! (minicom 실행 후 시리얼 모니터 확인 !!)
Tftp 이용 드라이버 받기 [먼저] Ubuntu 의 Host IP 확인 하기.. $tftp –r lcdtext.ko –g 192.168.123.100
Tftp 이용 어플리케이션 받기 [먼저] Ubuntu 의 Host IP 확인 하기.. $tftp –r lcdtest –g 192.168.123.100
파일 확인 실행 권한 변경 $ls $chmod 777 lcdtest
드라이버 모듈 올리고 확인하기 $insmod textlcd.ko <- 드라이버 모듈 올리기 $lsmod <- 드라이버 모듈 확인
Insmod ? Sys_create_module() 을 사용 드라이버 메모리 할당 Sys_get_kernel_syms()이용 드라이버 심볼을 커널에 등록 sys_init_module() 을 이용 메모리에 적재 Init_module() 실행 , register_chrdev() 가 수행 드라이버 이름과 메이저 번호와 file_operations 구조체를 커널 변수인 chrdevs 에 등록한다.
장치 노드 만들기(mknod) [형식] mknod /dev/device c [주번호] [부번호] 응용프로그램에서 장치노드를 오픈하여 사용한다. $mknod /dev/textlcd c 243 0 <- 노드생성 $cat /proc/devices <- 드라이버 포트 확인
드라이버 테스트하기 $./lcdtest
어플리케이션 프로그램 과제 [과제1] lcdtest.c 소스를 수정해서 1번줄에 성 2번째 줄에 이름을 쓰세요. [과제2] buzzer 제어 어플리케이션을 만드세요. 명령 ) buzzertest 0 <- buzzer off buzzertest 1 <- buzzer on Tip : /Android/driver/248.buzzer_driver 에 있는.. > Makefile 을 수정한다. (기존에 Makefile 을 복사..) > buzzertest.c 를 새롭게 작성한다. ( ledtest.c 의 내용을 복사한 후 장치 open 하는 부문을 /dev/buzzer 로 바꾼다. )
Buzzer 디바이스 드라이버 Make File CC = arm-linux-gcc obj-m := buzzer.o KDIR := /Android/linux-2.6.35-hanback PWD := $(shell pwd) TEST_TARGET = buzzertest TEST_SRCS = buzzertest.c default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules #$(CC) $(TEST_SRCS) -I$(KDIR)/include -o $@ $(CC) -o $(TEST_TARGET) $(TEST_SRCS) rm -f default clean: rm -f *.ko rm -f *.o rm -f *.mod.* rm -f .*.cmd rm -f $(TEST_TARGET)수정하기..
Buzzertest.c (1/2) #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc, char **argv) { int dev; char buff; // 아규먼트가 1개 이상이어야 한다. if(argc <= 1) { printf("please input the parameter ! ex)./buzzer 0 or 1 \n"); exit(1); } // 디바이스 드라이버 열기 dev = open("/dev/buzzer", O_WRONLY);
Buzzertest.c (2/2) if(dev != -1) { if(argv[1][0] == '0' && (argv[1][1] == 'x' || argv[1][1] == 'X')) { buff = (unsigned char)strtol(&argv[1][2],NULL,16); } // ledtest 0x 가 없이 ledtest 12 로 입력하면 12를 정수로 변환하여 출력 else { buff = atoi(argv[1]); write(dev,&buff,8) ; close(dev); } else { printf("Device Driver Open Error ! \n"); exit(1); return 0; // 정수형 main 함수 이므로 정수값을 리턴해 주어야 한다.