LED Device Control - mmap() Jo, Heeseung
디바이스제어 디바이스제어 HBE-SM5-S4210 에장착된 Peripheral 들은다양한인터페이스로연결되어있음 베이스보드에있는 LED 와 7-Segment 는 GPIO 로직접 CPU 에연결 M3 모듈인 FPGA 모듈은 FPGA 에의해서제어되며 CPU 와호스트버스로연결되어있어서프로그램에서는메모리인터페이스로인식 사용자가제어하려면다음그림에있는버튼을이용 - 앞으로의실습에서는꺼둔상태로작업 2
디바이스제어 동작모드선택스위치 3
mmap() 함수를이용한디바이스제어 mmap() 함수 응용프로그램에서직접하드웨어의 I/O 주소공간을직접적으로접근하도록해줌 다른함수들은유저영역과커널영역의메모리복사가필요 mmap() 함수는유저영역에서동작하는프로그램을커널영역의물리주소와매핑 mmap() 함수는가상주소를만들어서특정메모리어드레스에접근할수있도록해줌 4
mmap() 함수를이용한디바이스제어 mmap() 함수와 munmap() 함수의원형 #include <unistd.h> #include <sys/mman.h> void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *start, size_t length); mmap() 함수는 fd 로지정된파일에서 offset 을시작하는 length 바이트만큼을 start 주소로대응 start 주소는단지지정된해당주소로했으면좋겠다는의미이며, 보통 '0' 으로지정 mmap() 함수는지정된영역에대응된실제시작위치의포인터를반환 5
mmap() 함수를이용한디바이스제어 prot 인자는원하는메모리보호모드를설정 PROT_READ PROT_WRITE PROT_EXEC PROT_NONE : 페이지읽기허락 : 페이지쓰기허락 : 페이지실행 : 페이지접근불가 flags 인자는대응된객체의타입, 대응옵션과대응된페이지복사본에대한수정이해당프로세스에만보일것인지, 아니면다른참조하는프로세스와공유할것인지를설정 MAP_FIXED : 지정된주소이외의다른주소를선택하지않는다. MAP_SHARED : 객체에대응시키는다른모든프로세스와대응영역을공유 MAP_PRIVATE : 다른프로세스와대응영역을공유하지않는다. 6
LED 제어 HBE-SM5-S4210 에는총 8 개의 LED 가데이터버스와연결 정해진물리주소에값을출력하면해당비트가 1 인경우 LED 가켜짐 8bit LED 7
LED 제어 /working/mmap/bus-led 디렉터리에서 led.c 를작성 root@ubuntu:# mkdir -p /working/mmap/bus-led root@ubuntu:# cd /working/mmap/bus-led/ root@ubuntu:/working/mmap/bus-led# vi led.c 001: #include <stdio.h> 002: #include <stdlib.h> 003: #include <unistd.h> 004: #include <sys/types.h> 005: #include <sys/mman.h> 006: #include <sys/stat.h> 007: #include <fcntl.h> 008: #include <sys/ioctl.h> 009: #include <termios.h> 8
LED 제어 010: 011: #define FPGA_BASEADDRESS 0x05000000 012: #define LED_OFFSET 0x20 013: 014: static struct termios initial_settings, new_settings; 015: static int peek_character = -1; 016: 017: void init_keyboard() { 018: tcgetattr(0,&initial_settings); 019: new_settings = initial_settings; 020: new_settings.c_lflag &= ~ICANON; 021: new_settings.c_lflag &= ~ECHO; 022: new_settings.c_lflag &= ~ISIG; 023: new_settings.c_cc[vmin] = 1; 024: new_settings.c_cc[vtime] = 0; 025: tcsetattr(0,tcsanow,&new_settings); 026: } 027: 9
LED 제어 028: void close_keyboard() { 029: tcsetattr(0,tcsanow,&initial_settings); 030: } 031: 032: int kbhit() { 033: char ch; 034: int nread; 035: 036: if(peek_character!= -1) return 1; 037: 038: new_settings.c_cc[vmin] = 0; 039: tcsetattr(0,tcsanow,&new_settings); 040: nread = read(0,&ch,1); 041: new_settings.c_cc[vmin] = 1; 042: tcsetattr(0,tcsanow,&new_settings); 043: 10
LED 제어 044: if(nread == 1) { 045: peek_character = ch; 046: return 1; 047: } 048: return 0; 049: } 050: 051: int readch() { 052: char ch; 053: 054: if(peek_character!= -1) { 055: ch = peek_character; 056: peek_character = -1; 057: return ch; 058: } 059: read(0,&ch,1); 060: return ch; 061: } 062: 063: 11
LED 제어 064: int main() { 065: int fd; 066: unsigned short *addr_fpga, *addr_led; 067: unsigned short dir='0'; 068: unsigned short val, val2; 069: char ch = 'l'; 070: 071: if ((fd=open("/dev/mem",o_rdwr O_SYNC)) < 0) { 072: perror("mem open fail\n"); 073: exit(1); 074: } 075: 076: addr_fpga= (unsigned short *)mmap(null, 4096, 077: PROT_READ PROT_WRITE, MAP_SHARED, fd, FPGA_BASEADDRESS); 078: addr_led = addr_fpga + LED_OFFSET/sizeof(unsigned short); 079: 080: if(*addr_led == (unsigned short)-1) { 081: close(fd); 082: printf("mmap error\n"); 083: exit(1); 084: } 085: init_keyboard(); 12
LED 제어 088: printf(" --------------------------------------\n"); 089: printf(" 8bit LED IO Interface Procedure\n"); 090: printf(" --------------------------------------\n"); 091: printf(" [l] left shift\n"); 092: printf(" [r] right shift\n"); 093: printf(" [q] exit\n"); 094: printf(" --------------------------------------\n\n"); 095: 096: *addr_led = 0x100; 097: 098: val = 0; 099: val2 = 0; 100: 13
LED 제어 101: while(dir!= 'q') { 102: if(dir == 'l') { 103: val2 = (~(val >> 5)) & 0x1; 104: val = (val << 1) val2; 105: } 106: else if( dir == 'r') { 107: val2 = (~(val << 5)) & 0x80; 108: val = (val >> 1) val2; 109: } 110: 111: *addr_led = val 0x100; 112: usleep(80000); 113: 114: if(kbhit()) { 115: ch = readch(); 116: switch(ch) { 117: case 'r': dir = 'r'; break; 118: case 'l': dir = 'l'; break; 119: case 'q': dir = 'q'; break; 120: } 121: } 122: } 14
LED 제어 123: *addr_led = 0x00; 124: close_keyboard(); 125: 126: munmap(addr_fpga, 4096); 127: close(fd); 128: return 0; 129: } 15
LED 제어 Makefile 작성 root@ubuntu:/working/mmap/bus-led# vi Makefile -------------------------- 다음과같이작성한다 ------------------------- CC = arm-linux-gcc CFLAGS = -DNO_DEBUG EXEC=led OBJS=$(EXEC).o ####### Implicit rules.suffixes:.cpp.cxx.cc.c.c.cpp.o: $(CXX) -c $(CXXFLAGS) -o $@ $<.cxx.o: $(CXX) -c $(CXXFLAGS) -o $@ $<.cc.o: $(CXX) -c $(CXXFLAGS) -o $@ $<.C.o: $(CXX) -c $(CXXFLAGS) -o $@ $<.c.o: $(CC) -c $(CFLAGS) -o $@ $< all: $(EXEC) $(EXEC): $(OBJS) $(CC) -o $@ $^ clean: rm -f $(OBJS) $(EXEC) --------------------------- 저장하고종료한다 -------------------------- 16
LED 제어 make 명령어를실행하여소스를컴파일 실행파일 (led) 를 /tftpboot 에복사또는 nfs 사용 root@ubuntu:/working/mmap/bus-led# make arm-linux-gcc -c -DNO_DEBUG -o led.o led.c arm-linux-gcc -o led led.o root@ubuntu:/working/mmap/bus-led# ls Makefile led led.c led.o root@ubuntu:/working/mmap/bus-led# cp led /tftpboot/ 17
LED 제어 타겟보드에서실행파일 (led) 를다운 권한을변경한후실행 [root@sm5s4210 ~]# tftp -r led -g 192.168.0.100 [root@sm5s4210 ~]# ls -l -rw-r--r-- 1 root root 8427 Aug 24 11:40 led [root@sm5s4210 ~]# chmod 777 led [root@sm5s4210 ~]#./led -------------------------------------- 8bit LED IO Interface Procedure -------------------------------------- [l] left shift [r] right shift [q] exit -------------------------------------- 18
LED Device Control - Device driver Jo, Heeseung
디바이스드라이버구현 : LED HBE-SM5-S4210 M3 Module 에는총 8 개의 LED 가데이터버스와연결 정해진물리주소에값을출력하면해당비트가 1 인경우 LED 가켜짐 8bit LED 20
디바이스드라이버구현 : LED LED Controller 는 8 비트로구성 FPGA 에 8 비트 LED 를제어하기위해서 8 비트의레지스터가존재 LED 제어레지스터의각비트에 '0' 또는 '1' 을써넣음으로써 On/Off 할 LED 를선택 Physical Address 0x0500_0020 LED_Ctrl_Reg BIT 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Reserved LED_ EN LED_ 6 LED_ 7 LED_ 8 Reset X X X X X X X X X X X X X X X 0 Bits Name Description LED_ 5 0 LED_1 User LED Display Bit 0 (Active High) 1 LED_2 User LED Display Bit 1 (Active High) 2 LED_3 User LED Display Bit 2 (Active High) 3 LED_4 User LED Display Bit 3 (Active High) 4 LED_5 User LED Display Bit 4 (Active High) 5 LED_6 User LED Display Bit 5 (Active High) 6 LED_7 User LED Display Bit 6 (Active High) 7 LED_8 User LED Display Bit 7 (Active High) LED_ 4 LED_ 3 LED_ 2 LED_ 1 8 LED_EN LED Enable Bit (Active High) 21
디바이스드라이버구현 : LED 소스파일작성 LED 리눅스디바이스드라이버는커널에포함 리눅스환경에서설정가능 기본적으로제공되는커널내부에소스코드가포함되어있고드라이버사용여부를설정가능 root@ubuntu:/working/linux-2.6.35-s4210# make menuconfig 22
디바이스드라이버구현 : LED 소스파일작성 ( 설정, 저장, 종료 ) Device Drivers->[*] Misc devices-> [*] Hanback Electronics M3(FPGA) Device Support-><*>LED(8 bit) 1 2 4 3 23
디바이스드라이버구현 : LED 소스파일작성 소스코드는 /working/linux-2.6.35-s4210/drivers/misc/hanback/ root@ubuntu:/working/linux-2.6.35-s4210# cd drivers/misc/hanback root@ubuntu:/working/linux-2.6.35-s4210/drivers/misc/hanback# vi led.c #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <asm/uaccess.h> #include <linux/fs.h> #include <asm/io.h> #include <linux/ioport.h> #include <linux/kdev_t.h> #include <linux/miscdevice.h> #include <linux/platform_device.h> #include <asm/ioctl.h> #include <asm/io.h> 24
디바이스드라이버구현 : LED #define DRIVER_AUTHOR #define DRIVER_DESC #define LED_NAME #define LED_MODULE_VERSION #define LED_ADDRESS #define LED_ADDRESS_RANGE "Hanback Electronics" "led test program" "led" "LED V1.0" 0x05000020 0x1000 //Global variable static int led_usage = 0; static unsigned long *led_ioremap; // define functions... int led_open(struct inode *minode, struct file *mfile) { if(led_usage!= 0) return -EBUSY; led_ioremap= ioremap(led_address,led_address_range); if(!check_mem_region((unsigned long)led_ioremap,led_address_range)){ request_mem_region((unsigned long)led_ioremap,led_address_range,led_name); } 25
디바이스드라이버구현 : LED else printk(kern_warning"can't get IO Region 0x%x\n", (unsigned int)led_ioremap); led_usage = 1; return 0; } int led_release(struct inode *minode, struct file *mfile) { iounmap(led_ioremap); release_mem_region((unsigned long)led_ioremap,led_address_range); led_usage = 0; return 0; } 26
디바이스드라이버구현 : LED ssize_t led_write_byte(struct file *inode, const char *gdata, size_t length, loff_t *off_what) { unsigned short *addr; unsigned char c; get_user(c,gdata); addr = (unsigned short*)led_ioremap; *addr = c 0x100; return length; } static struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.write = led_write_byte,.release = led_release, }; static struct miscdevice led_driver = {.fops = &led_fops,.name = LED_NAME,.minor = MISC_DYNAMIC_MINOR, }; 27
디바이스드라이버구현 : LED int led_init(void) { misc_register(&led_driver); return 0; } void led_exit(void) { misc_deregister(&led_driver); printk(kern_info"driver: %s DRIVER EXIT\n", LED_NAME); } module_init(led_init); module_exit(led_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("Dual BSD/GPL"); 28
디바이스드라이버구현 : LED 소스파일작성 드라이버를작성한후커널을컴파일하면드라이버는커널내부에이미지내부에포함되어컴파일됨 - 주번호와부번호는커널부팅시에자동으로할당 29
디바이스드라이버구현 : LED 드라이버테스트프로그램작성 root@ubuntu:/working/device_driver/bus-led_driver# vi led_test.c 001: #include <stdio.h> 002: #include <stdlib.h> 003: #include <unistd.h> 004: #include <sys/types.h> 005: #include <sys/stat.h> 006: #include <fcntl.h> 007: 008: int main(int argc, char **argv) 009: { 010: int dev; 011: char buff; 012: 013: if(argc <= 1) { 014: printf("please input the parameter! ex)./led_test 0xff\n"); 015: return -1; 016: } 30
디바이스드라이버구현 : LED 017: 018: dev = open("/dev/led", O_WRONLY); 019: 020: if (dev!= -1) { 021: if(argv[1][0] == '0' && (argv[1][1] == 'x' argv[1][1] == 'X' )) 022: buff = (unsigned short)strtol(&argv[1][2],null,16); 023: else 024: buff = atoi(argv[1]); 025: 026: write(dev,&buff,2); 027: close(dev); 028: } 029: else { 030: printf( "Device Open ERROR!\n"); 031: return -1; 032: } 033: return 0; 034: } 31
디바이스드라이버구현 : LED root@ubuntu:/working/device_driver/bus-led_driver# vi Makefile CC = arm-linux-gcc CFLAGS = -DNO_DEBUG EXEC=led_test OBJS=$(EXEC).o ####### Implicit rules.suffixes:.cpp.cxx.cc.c.c.cpp.o: $(CXX) -c $(CXXFLAGS) -o $@ $<.cxx.o: $(CXX) -c $(CXXFLAGS) -o $@ $<.cc.o: $(CXX) -c $(CXXFLAGS) -o $@ $<.C.o: $(CXX) -c $(CXXFLAGS) -o $@ $<.c.o: $(CC) -c $(CFLAGS) -o $@ $< all: $(EXEC) $(EXEC): $(OBJS) $(CC) -o $@ $^ clean: rm -f $(OBJS) $(EXEC) Makefile 작성 32
디바이스드라이버구현 : LED 컴파일 (make) root@ubuntu:/working/device_driver/bus-led_driver# ls Makefile led_test.c root@ubuntu:/working/device_driver/bus-led_driver# make clean;make rm -f led_test.o led_test arm-linux-gcc -c -DNO_DEBUG -o led_test.o led_test.c arm-linux-gcc -o led_test led_test.o root@ubuntu:/working/device_driver/bus-led_driver# ls Makefile led_test led_test.c led_test.o 33
디바이스드라이버구현 : LED 타겟보드에다운받기및실행 타겟보드에다운로드받는방법으로 tftp 를사용또는 nfs 사용 root@ubuntu:/working/device_driver/bus-led_driver# cp led_test /tftpboo t 34
디바이스드라이버구현 : LED 타겟보드에다운받기및실행 /tftpboot 로복사가완료되면타겟보드에서아래와같이명령어를입력 [root@sm5s4210 ~]$mkdir -p /root/device_driver [root@sm5s4210 ~]$cd /root/device_driver/ [root@sm5s4210 device_driver]$ tftp -r led_test -g 192.168.0.100 [root@sm5s4210 device_driver]$ ls -l ------------------ 다음과같은메시지가출력된다 ----------------- -rw-r--r-- 1 root root 6508Sep 1 21:42 led_test [root@sm5s4210 device_driver]$ chmod +x led_test [root@sm5s4210 device_driver]$ ls -l ------------------ 다음과같은메시지가출력된다 ----------------- -rwxr-xr-x 1 root root 6508Sep 1 21:42 led_test 35
디바이스드라이버구현 : LED 타겟보드에다운받기및실행 led_test 를다운받고 chmod 명령어를사용하여실행권한변경 [root@sm5s4210 device_driver]$./led_test please input the parameter! ex)./test 0xff [root@sm5s4210 device_driver]$./led_test 0x11 led_test 프로그램실행후 LED 제어테스트 36