1. 디바이스드라이버 [ GPIO ] 1.1. GPIO 란 GPIO(General Purpose Input/Output) 란일반적인용도로사용가능한디지털입출력기능의 Port pins 이다. S3C2410의 GPIO는총 117개이며각각이 pin 들은 input/output으로프로그램되거나인터럽트 source로사용될수있다. S3C2410의대부분의 GPIO는단순히디지털입출력뿐만아니라부가적인기능을갖고있다. 그래서다른기능을사용하다보면처음생각보다단순 IO로사용할 GPIO가적어짐을알수있다. 따라서하드웨어설계자는 GPIO를사용할때이점을주의해야한다. 출력에관계된정확한사양은레퍼런스매뉴얼의하드웨어특성에관련된사항을참조하기바란다. 1.2. GPIO 레지스터설명 S3C2410 의 GPIO Port 는다음과같다. - Port A (GPA) : 23-output port - Port B (GPB) : 11-input/output port - Port C (GPC) : 16-input/output port - Port D (GPD) : 16-input/output port - Port E (GPE) : 16-input/output port - Port F (GPF) : 8-input/output port - Port G (GPG) : 16-input/output port - Port H (GPH) : 11-input/output port
정확한레지스터의이름과주소는다음과같다. 레지스터주소 이름 접근 설명 0x56000000 GPACON 읽기 / 쓰기 GPA[22..0] 출력 / 부가기능사용허가설정레지스터 0x56000004 GPADAT 읽기 / 쓰기 GPA[22..0] DATA 출력레지스터 0x56000010 GPBCON 읽기 / 쓰기 GPB[10..0] 입력 / 출력 / 부가기능사용허가설정레지스터 0x56000014 GPBDAT 읽기 / 쓰기 GPB[10..0] DATA 입력 / 출력레지스터 0x56000018 GPBUP 읽기 / 쓰기 GPB[10..0] Pull-up disable 레지스터 0x56000020 GPBCON 읽기 / 쓰기 GPC[15..0] 입력 / 출력 / 부가기능사용허가설정레지스터 0x56000024 GPBDAT 읽기 / 쓰기 GPC[15..0] DATA 입력 / 출력레지스터 0x56000028 GPBUP 읽기 / 쓰기 GPC[15..0] Pull-up disable 레지스터 0x56000030 GPBCON 읽기 / 쓰기 GPD[15..0] 입력 / 출력 / 부가기능사용허가설정레지스터 0x56000034 GPBDAT 읽기 / 쓰기 GPD[15..0] DATA 입력 / 출력레지스터 0x56000038 GPBUP 읽기 / 쓰기 GPD[15..0] Pull-up disable 레지스터 0x56000040 GPBCON 읽기 / 쓰기 GPE[15..0] 입력 / 출력 / 부가기능사용허가설정레지스터 0x56000044 GPBDAT 읽기 / 쓰기 GPE[15..0] DATA 입력 / 출력레지스터 0x56000048 GPBUP 읽기 / 쓰기 GPE[15..0] Pull-up disable 레지스터 0x56000050 GPBCON 읽기 / 쓰기 GPF[7..0] 입력 / 출력 / 부가기능사용허가설정레지스터 0x56000054 GPBDAT 읽기 / 쓰기 GPF[7..0] DATA 입력 / 출력레지스터 0x56000058 GPBUP 읽기 / 쓰기 GPF[7..0] Pull-up disable 레지스터 0x56000060 GPBCON 읽기 / 쓰기 GPG[15..0] 입력 / 출력 / 부가기능사용허가설정레지스터 0x56000064 GPBDAT 읽기 / 쓰기 GPG[15..0] DATA 입력 / 출력레지스터 0x56000068 GPBUP 읽기 / 쓰기 GPG[15..0] Pull-up disable 레지스터 0x56000070 GPBCON 읽기 / 쓰기 GPH[10..0] 입력 / 출력 / 부가기능사용허가설정레지스터 0x56000074 GPBDAT 읽기 / 쓰기 GPH[10..0] DATA 입력 / 출력레지스터 0x56000078 GPBUP 읽기 / 쓰기 GPH[10..0] Pull-up disable 레지스터
1.3. 레지스터의설정값정의 GPACON [ 입 / 출력 / 부가기능사용허가설정레지스터 ] 이레지스터는 GPIO pin 들을입력설정할것인지, 출력으로설정할것인지, 부가적인기능을사용할것인지를결정한다. 0로해당 bit가설정되면출력이가능하게되어 GPADAT 레지스트에어떤값을넣어서제어할수있다. 1로해당 bit가설정되면부가적인기능이가능하게된다. 이레지스터는 RESET시에는 1로 bit가설정된다. 이레지스트는 NAND, ncs, address 신호의부가적인기능을사용하므로이레지스트를변경하여출력으로사용하지않는것이바람직하다. GPBCON - GPHCON [ 입 / 출력 / 부가기능사용허가설정레지스터 ] 이레지스터는 GPIO pin 들을입력설정할것인지, 출력으로설정할것인지, 부가적인기능을사용할것인지를결정한다. 0으로해당 bit가설정되면입력전용으로설정되어 GPBDAT GPHDAT 레지스트를통해서 GPIO pin 상태를읽을수있다. 1로해당 bit가설정되면출력이가능하게되어 GPBDAT GPHDAT 레지스트에어떤값을넣어서제어할수있다. 2로해당 bit가설정되면부가적인기능이가능하게된다. 이레지스터는 RESET시에는 0으로 bit가설정된다. GPBUP - GPHCON [ Pull-up 레지스터의 enable/disable 설정 ] 이레지스터는 GPIO의 Pull-up 을허가또는금지를설정한다. 1로해당 bit가설정되면 Pull-up 레지스터는 disable 된다. 0으로 bit를설정하면 Pull-up 레지스터는 enable 된다. 이레지스터는 RESET시에는 0으로 bit가설정된다. 위와같은 GPIO 레지스터들은커널의 include/asm-arm/arch-s3c2410/regs-gpio.h에정의되어있다.
1.4. GPIO 를이용한 LED 구동및스위치동작확인 간단하게타겟보드에서디버깅용으로달려있는 4개의 LED를가지고 GPIO의입출력을 TEST 하는방법을소개하겠다. 회로도 3.3V EINT[0..8] EINT[0..8] EINT4 EINT5 EINT6 EINT7 R80 R81 R82 R83 1K 1K 1K 1K D8 D9 LED LED D10 LED D11 LED 회로도에서보면타겟보드의디버깅용으로사용한 LED가연결되어있는 GPIO 는 GPF4, GPF5, GPF6, GPF7 이다. 준비조건 모든작업디렉토리는다음과같다. 커널작업디렉토리 : /project/ez-s2410/kernel/linux 디바이스드라이버디렉토리 : /project/ ez-s2410/gpio 어플리케이션디렉토리 : /project/ ez-s2410/gpio 헤더파일디렉토리 : /project/ ez-s2410/gpio NFS(Network File System) 를사용할경우에는 NFS 가마운트되어있어야한다. HowTo 문서중 [ 커널설치및패치 ] 의내용을탐독하시고, 커널을컴파일해야한다. 물론제공한 CD에서패치한커널을복사하여사용하여도상관없다. 문서내용중 " /project/ez-s2410/kernel/linux 이란디렉토리에설치하는것을가정하여설명한다. " 는말이있다. 이것은 Makefile을만들때중요한위치가된다. 이내용에관해서는아래 Makefile 만들기에서설명하기로한다. 다음화면은커널컴파일후 /project/ez-s2410/kernel/linux 의리스트이다. [.config 파일이있는지꼭확인하기바란다.]
S3C2410를위한 ARM 용크로스컴파일러가설치되어있어야한다. 컴파일에필요한파일들을확인하자.
디바이스드라이버 디바이스드라이버용 Makefile을만든다. 작업공간은 /project/ ez-s2410/gpio 디렉토리이다. # vi Makefile # # kernel 2.6 driver Makefile # obj-m := dev_gpio.o KDIR := /project/ez-s2410/kernel/linux PWD := $(shell pwd) SP_APP = app_gpio default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules cp -f $(obj-m:.o=.ko) /nfs/sap/. arm-linux-gcc -o $(SP_APP) $(SP_APP).c cp -f $(SP_APP) /nfs/sap clean : rm -rf *.ko rm -rf *.mod.* rm -rf.*.cmd rm -rf *.o obj-m := dev_gpio.o kernel-2.6대에서모듈컴파일을위해서필요하다. 모듈은.ko 형태로생성된다. KDIR = /project/ez-s2410/kernel/linux 컴파일에사용될커널디렉토리를적어준다. $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules 모듈을컴파일한다. arm-linux-gcc -o $(SP_APP) $(SP_APP).c 어플리케이션을컴파일한다.
디바이스드라이버를만든다. [dev_gpio.c ] 작업공간은 /project/ ez-s2410/gpio 디렉토리이다. # vi gpio.c - 파일 : dev_gpio.c 설명 : 디바이스드라이버예제 kernel 2.6 버젼으로작성되었다. 작성 : ez-s2410 보드의 GPIO 핀 GPF[4..7] 에는디버그용 LED 가연결되어있다. GPF4, GFP5 : 입력 GPF6, GFP7 : 츨력 ------------------------------------------------------------------------------*/ #include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/version.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/wait.h> #include <linux/ioport.h> #include <linux/slab.h> #include <linux/poll.h> #include <linux/proc_fs.h> #include <asm/system.h> #include <asm/uaccess.h> #include <asm/hardware.h> #include <asm/irq.h> #include <asm/ioctl.h> #include <asm/unistd.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include "dev_gpio.h" /* 로컬상수정의 --------------------------------------------------------------*/ #define DEV_NAME "dev-gpio" #define DEV_VERSION DEV_NAME" V01" /* 로컬변수정의 --------------------------------------------------------------*/ static int showmsg = 0; static int major = DEV_MAJOR_DEF; /* 매크로정의 ----------------------------------------------------------------*/ #define reg_s2410(x) *(volatile unsigned long *)(x) #define set_gpio_in(reg, nr) reg &= ~(3<<(nr)*2) #define set_gpio_out(reg, nr) reg &= ~(3<<(nr)*2); reg = (1<<(nr)*2) #define GPIO_IN(reg,nr) (reg & (1<<(nr))? 1:0) #define GPIO_OUT(reg,nr,val) reg &= ~(1<<(nr)); reg = (val&0x1)<<(nr);
/*implementation ========================================================*/ 설명 : GPIO 입출력을설정한다. 주의 : void hw_gpf_init( void ) set_gpio_in( reg_s2410(s3c2410_gpfcon), 4 ); set_gpio_in( reg_s2410(s3c2410_gpfcon), 5 ); set_gpio_out( reg_s2410(s3c2410_gpfcon), 6 ); set_gpio_out( reg_s2410(s3c2410_gpfcon), 7 ); // 내부 pull-up disable 7..4 reg_s2410(s3c2410_gpfup) = 0xf0; // led off GPIO_OUT( reg_s2410(s3c2410_gpfdat), 6, 1 ); GPIO_OUT( reg_s2410(s3c2410_gpfdat), 7, 1 ); 설명 : GPIO 출력주의 : void hw_gpf_out( int gpnr, int val ) GPIO_OUT( reg_s2410(s3c2410_gpfdat), gpnr, val ); 설명 : GPIO 입력주의 : int hw_gpf_in( int gpnr ) return GPIO_IN( reg_s2410(s3c2410_gpfdat), gpnr ); 설명 : 어플리케이션에서디바이스를 open 하였을때호출되는함수주의 : int gpio_open( struct inode *inode, struct file *filp ) try_module_get(this_module); if (showmsg) printk(" %s OPEN\n", DEV_NAME ); return 0; 설명 : 어플리케이션에서디바이스를 close를하였을때호출되는함수주의 : int gpio_release( struct inode *inode, struct file *filp) module_put(this_module); if (showmsg) printk(" %s CLOSE\n", DEV_NAME ); return 0;
설명 : ioctl 함수 int gpio_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) switch (cmd) case IOCTL_GPIO_IN : if ( ( 4 <= arg ) && ( arg <= 5 ) ) return hw_gpf_in( arg ); return -1; case IOCTL_GPIO_OUT_SET : if ( ( 6 <= arg ) && ( arg <= 7 ) ) hw_gpf_out( arg, 1 ); return 0; return -1; case IOCTL_GPIO_OUT_CLR : if ( ( 6 <= arg ) && ( arg <= 7 ) ) hw_gpf_out( arg, 0 ); return 0; return -1; return -EINVAL; 드라이버에사용되는접근함수에대한함수포인터구조체를정의한다. 이구조체는 fs.h에정의되어있다. static struct file_operations gpio_fops =.open = gpio_open,.release = gpio_release,.ioctl = gpio_ioctl, ; 설명 : init int gpio_init( void ) // 장치를등록한다. ============================================ major &= 0xff; if(!register_chrdev( major, DEV_NAME, &gpio_fops ) ) printk(" register device %s OK (major=%d)\n", DEV_VERSION, major ); else printk(" unable to get major %d for %s\n", major, DEV_NAME ); return -EBUSY; hw_gpf_init(); return 0;
설명 : exit 주의 : void gpio_exit( void ) unregister_chrdev( major, DEV_NAME ); printk(" unregister %s OK\n", DEV_NAME ); */ module_init(gpio_init); module_exit(gpio_exit); MODULE_PARM(major, "i"); MODULE_PARM(showmsg, "i"); MODULE_AUTHOR("freefrug@falinux.com"); MODULE_LICENSE("Dual BSD/GPL"); /* end */ 헤더파일을만든다. [ gpio.h ] 작업공간은 /project/ez-x5/test/gpio/include 디렉토리이다. # vi gpio.h //------------------------------------------------------------------------------ // 파일 : dev_gpio.c // 설명 : 디바이스드라이버예제 // kernel 2.6.x 버젼으로작성되었다. // // 작성 : 오재경 freefrug@falinux.com //------------------------------------------------------------------------------ #ifndef _DEV_GPIO_H_ #define _DEV_GPIO_H_ /* 모듈관련 -------------------------------------------------------------------*/ #define DEV_MAJOR_DEF 240 /* ioctl 함수의 command -------------------------------------------------------*/ #define MAGIC_GPIO 's' #define IOCTL_GPIO_IN _IOW( MAGIC_GPIO, 10, int ) #define IOCTL_GPIO_OUT_SET _IOW( MAGIC_GPIO, 11, int ) #define IOCTL_GPIO_OUT_CLR _IOW( MAGIC_GPIO, 12, int ) #endif // _DEV_GPIO_H_
어플리케이션 어플리케이션프로그램을만든다. [ test.c ] 작업공간은 /project/ ez-s2410/gpio 디렉토리이다. # vi app_gpio.c //------------------------------------------------------------------------------ // 파일 : app_gpio.c // 설명 : //------------------------------------------------------------------------------ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/signal.h> #include <sys/ioctl.h> #include <sys/poll.h> #include "dev_gpio.h" #define NODE_FILE "/dev/gpio" //------------------------------------------------------------------------------ // 설명 : main // 주의 : //------------------------------------------------------------------------------ int main( int argc, char **argv ) int fd; // 파일핸들 fd = open( NODE_FILE, O_RDWR O_NONBLOCK ); if ( fd < 0 ) perror( NODE_FILE" open :"); exit(0); printf( "app_gpio start...\n" ); // led ON ioctl( fd, IOCTL_GPIO_OUT_CLR, 6 ); ioctl( fd, IOCTL_GPIO_OUT_CLR, 7 ); // led OFF ioctl( fd, IOCTL_GPIO_OUT_SET, 6 ); ioctl( fd, IOCTL_GPIO_OUT_SET, 7 ); sleep(1); sleep(1); while(1) if ( 0 == ioctl( fd, IOCTL_GPIO_IN, 4 ) ) ioctl( fd, IOCTL_GPIO_OUT_CLR, 6 ); ioctl( fd, IOCTL_GPIO_OUT_SET, 6 ); ioctl( fd, IOCTL_GPIO_OUT_CLR, 7 ); ioctl( fd, IOCTL_GPIO_OUT_SET, 7 ); sleep(1); sleep(1);
if ( 0 == ioctl( fd, IOCTL_GPIO_IN, 5 ) ) ioctl( fd, IOCTL_GPIO_OUT_CLR, 6 ); ioctl( fd, IOCTL_GPIO_OUT_CLR, 7 ); break; close( fd ); printf( "app_gpio end\n" ); return 0; 여기까지 EZ-S2410에서 GPIO를이용한 TEST 할수있는디바이스드라이버와어플리케이션을만들었다. 이소스는제공한 CD에들어있다. 컴파일 작업공간은 /project/ez-x5/test/gpio/include 디렉토리이다. # make clean # make
위와같이컴파일이정상적으로수행이되고나면다음과같은파일들이생성된다. 여기서필요한모듈과실행파일은다음과같다. 모듈 : dev_gpio.ko 실행파일 : app_gpio 타겟보드에디바이스드라이버및어플리케이션다운로드하기 타겟보드에다운로드하는방법은두가지가있다. 첫번째는 NFS를이용하는방법이있다. 이것은디버깅을하기에유용하다. 두번째는램디스크이미지에넣는방법이있다. 이것은디버깅작업을할때는매우귀찮은작업이다. 첫번째방법은 NFS를마운트되어있다는전제조건하에마운트된디렉토리에컴파일한 dev_gpio.ko와 app_gpio파일을복사하여모듈을적재하고어플리케이션을실행시키면된다. $ insmod dev_gpio.ko $./ app_gpio [root@falinux ~]$ mount -t nfs -o nolock 192.168.10.26:/nfs /mnt/nfs/ nfs warning: mount version older than kernel [root@falinux ~]$ [root@falinux ~]$ cd /mnt/nfs/sap/ insmod dev_gpio.ko Using dev_gpio.ko register device dev-gpio V01 OK (major=240)./app_gpio /dev/gpio open :: No such file or directory mknod /dev/gpio c 240 0./app_gpio app_gpio start...
두번째방법은램디스크이미지를이용하는방법에대하여설명하겠다. 램디스크이미지복사하기 타겟보드에올라가있는램디스크를가져오자. 만일없다면제공한 CD에서복사를하자. 자세한설명은 [10 장램디스크이미지제작 ] 을참고하기바란다. 단지여기서는작업내용만을적어놓겠다. 모든작업디렉토리는 /project/ez-s2410/ramdisk 이며, 디렉토리를만든다. # mkdir /project/ez-s2410/ramdisk 램디스크이미지를복사한다. # cp -a /mnt/cdrom/sw/ramdisk/ramdisk-12m.gz /project/ez-s2410/ramdisk/ 작업디렉토리로이동한다. # cd /project/ez-s2410/ramdisk 램디스크이미지압축풀기 램디스크이미지압축을풀어마운트할작업디렉토리를만든다. # mkdir ramdisk_dir 램디스크이미지압축을푼다. # gzip d ramdisk-12m.gz 압축을푼램디스크이미지를마운트한다. # mount t ext2 o loop ramdisk-12m ramdisk_dir 램디스크이미지를수정한다. 디바이스노드를만든다. # cd ramdisk_dir # mknod /dev/gpio c 240 0
디바이스드라이버와어플리케이션프로그램을복사할디렉토리를만든다. # pwd /project/ez-s2410/ramdisk/ramdisk_dir # mkdir gpio # cd gpio # cp a /project/ez-s2410/gpio/dev_gpio.ko. # cp a /project/ez-s2410/gpio/app_gpio. 램디스크이미지압축하기원하는대로램디스크를만든후에다음의작업을하여압축한다. 마운트를해제한다. # pwd /project/ez-s2410/ # umount ramdisk_dir 램디스크이미지압축한다. # gzip ramdisk-12m 램디스크이미지를타겟보드로다운로드한다. 다운로드하는방법은앞장에설명되어있다. 이를참고하기바란다. 모듈적재및프로그램실행 타겟보드에램디스크를다운로드하였다면타겟보드를다시부팅하고로그인하여타겟보드에 gpio 디렉토리가생성되고, 이디렉토리안에 dev_gpio.ko 와 app_gpio 파일이존재할것이다. 모듈적재하기모듈적재는 insmod 명령을사용한다. $ cd /gpio/ $ insmod dev_gpio.ko 만약 /dev/gpio라는파일시스템이없으면만들어주어야한다. $ ls /dev/ grep gpio $ mknod /dev/gpio c 240 0
모듈해지는 rmmod 명령을사용한다. $ rmmod dev_gpio.ko 다음은위의내용을실행한화면이다. [root@falinux ~]$ mount -t nfs -o nolock 192.168.10.26:/nfs /mnt/nfs/ nfs warning: mount version older than kernel [root@falinux ~]$ [root@falinux ~]$ cd /mnt/nfs/sap/ insmod dev_gpio.ko Using dev_gpio.ko register device dev-gpio V01 OK (major=240)./app_gpio /dev/gpio open :: No such file or directory mknod /dev/gpio c 240 0 rmmod dev_gpio unregister dev-gpio OK 어플리케이션프로그램실행하기모듈이적재되었고, 디바이스드라이버노드가만들어져있다면어플리케이션프로그램을실행시켜 GPIO를이용한 LED의입 / 출력 TEST를하자. $ insmod gpio_dev.o $./app_gpio [root@falinux ~]$ mount -t nfs -o nolock 192.168.10.26:/nfs /mnt/nfs/ nfs warning: mount version older than kernel [root@falinux ~]$ [root@falinux ~]$ cd /mnt/nfs/sap/ insmod dev_gpio.ko Using dev_gpio.ko register device dev-gpio V01 OK (major=240)./app_gpio app_gpio start...
참고사항 위에서모두설명한내용이지만다시한번참고하기바란다. 1) insmod dev_gpio.ko 실행시다음과같은경고메시지 insmod dev_gpio.ko Using dev_gpio.ko Warning: loading gpio_dev will taint the kernel: no license See http://www.tux.org/lkml/#export-tainted for information about tainted modules 이경고메시지는디바이스드라이버의 modules license 에관련한경고이다. 이문제의해결은디바이스드라이버의마지막에다음의코드를추가해주면된다. [ FALINUX에서는이부분이제거된상태로제공된다 ] MODULE_LICENSE("Dual BSD/GPL "); MODULE_PARM(major, "i"); MODULE_PARM(showmsg, "i"); MODULE_AUTHOR("freefrug@falinux.com"); MODULE_LICENSE("Dual BSD/GPL"); 2) 어플리케이션실행시 GPIO Open Fail 이나올경우./app_gpio /dev/gpio open :: No such file or directory 이문제는디바이스장치파일이없어서나는에러메시지이다. 다음과같이파일시스템을 /dev 디렉토리내에만들어주면된다. mknod /dev/gpio c 240 0