====== 이지보드-X5 GPIO 제어하기 ====== ====== GPIO 란 ====== 본격적인 내용에 들어가기에 앞서, 여기서 제어해 볼 GPIO 라는 것에 대해 살펴보도록 하자. 지극히 내가 알고 있는 한도내에서 설명할 테니, 주의하기 바란다. 내가 졸업작품으로 스탠드 제어를 했을 때, 바로 GPIO 를 이용했다. 그 땐, GPIO 가 뭔지도 모른 체, 따라하기에 바빴다. 하지만, 회사에 들어와서 POD 를 GPIO 로 제어해 보고는 '이제 알듯 말듯 하구나' 했다. GPIO 는 어떤 특정 하드웨어를 위한 디바이스가 아니다. GPIO 를 이용해서 우리는 어떤 하드웨어든지 제어할 수 있다. GPIO 가 Genernal Purpose Input Output 의 약자인 것만 봐도 알 수 있다. 쉽게 생각하면, 아무리 복잡한 신호를 필요로 하는 하드웨어라도 결국에는 1 아니면 0 이다. GPIO 를 통해서 우리는 1 또는 0 을 내보낼 수 있다. 이 사실만으로도 우리는 원하는 결과를 얻어낼 수 있다. 본격적으로 설명하게될 소스를 이해하기 위해서는 좀더 세부적인 GPIO 에 대한 이해가 필요하다. 제목에서도 보았듯이, 이지보드를 이용할 것이기 때문에, Xscale CPU 의 GPIO 에 대해서 알아야 한다. 다음의 사이트를 참고하기 바란다. http://www.falinux.com/win/01_hw/030_xscale/gpio.htm http://falinux.com/pds/x5.html 이해하는 데 많은 도움이 될 것이다. 앞에서 잠깐 얘기했지만, GPIO 를 통해서 내가 원하는 신호를 내보낼 수가 있다고 했었다. 또한 특정 신호가 들어왔는지 확인도 가능하다. 실제로 해보면, 말처럼 하기란 쉽지 않다. GPIO 는 몇가지 레지스터 핀으로 구성되어 있다. ===== 핀의 특성 부여용 레지스터 ===== | 이름 | 값 | 설명 | | GPDR | 1 | 출력포트로 세팅 | | | 0 | 입력포트로 세팅 | | GAFR | 1 | 부가기능을 사용 | | | 0 | 부가기능을 사용하지 않음 | | GRER | 1 | 상승 엣지 검출 기능을 활성화 | | | 0 | 엣지 검출 기능을 사용하지 않음 | | GFER | 1 | 하강 엣지 검출 기능을 활성화 | | | 0 | 엣지 검출 기능을 사용하지 않음 | ===== 데이터 출력 및 검출용 레지스터 ===== | 이름 | 값 | 설명 | | GPSR | 1 | HIGH(1) 값을 출력 | | | 0 | 이전 상태 유지 | | GPCR | 1 | LOW(0) 값을 출력 | | | 0 | 이전 상태 유지 | | GPLR | 1 | 입력에 3.3V 가 인가된 상태 | | | 0 | 입력에 0V 가 인가된 상태 | | GEDR | 1 | 입력값의 에지 검출 활성화 | | | 0 | 의미 없음 | 언뜻 보기에 위의 8 가지 레지스터를 잘 사용하면, 원하는 것을 할 수 있을 것 같다. 8 가지의 레지스터를 이해했다면, GPIO 를 이해했다고 봐도 무방하다. ====== 준비하기 ====== 이제 본격적으로 GPIO 를 제어해보자!! 여기서 사용하는 프로그램은 아래의 주소에서 다운로드 받을 수 있다. http://falinux.com/pds/x5.html 친절하게도(?) 이지보드에는 GPIO 를 테스트할 수 있게끔, 4개의 LED 를 장착해서 제공하고 있다. 아마도 크로스컴파일 환경에서 컴파일하고 실행하는 것만으로도 GPIO 제어를 손쉽게 할 수 있을 것이다. 하지만, 무엇보다 중요한 것은 원리는 아는 것이다. ====== 소스 분석 ====== 가장 중요하다고 할 수 있는 3 개의 파일을 살펴보기로 하자. ===== 1. gpio.h ===== 이 파일은 이지보드 PXA255 의 GPIO 관련 레지스터 번지를 정의하고 있다. 전체 GPIO 의 레지스터의 주소를 알고 싶으면, #vi 커널경로/include/asm-arm/arch-pxa/pxa-regs.h 보기 바란다. 여기서 주의할 것은, 기본 베이스 커널에 pxa255 관련 패치를 해주지 않으면 아마도 파일이 존재하지 않을 것이다. 이지보드에서 기본적으로 제공해주는 커널은 패치가 되어있기 때문에 바로 볼 수 있다. #ifndef _GPIODEV_HEADER_ #define _GPIODEV_HEADER_ //메이저 번호 동적 할당 #define GPIO_MAJOR 253 // 메이저 번호에 254번 할당. #define DEVICE_NAME "gpio" // GPIO 디바이스 이름. // GPIO 출력 #define GPIO_LED_1 2 // GPIO2 #define GPIO_LED_2 3 // GPIO3 #define GPIO_LED_3 4 // GPIO4 #define GPIO_LED_4 5 // GPIO5 // GPIO 출력 마스크 #define MASK_GPIO_LED_1 ( 1 << GPIO_LED_1 ) #define MASK_GPIO_LED_2 ( 1 << GPIO_LED_2 ) #define MASK_GPIO_LED_3 ( 1 << GPIO_LED_3 ) #define MASK_GPIO_LED_4 ( 1 << GPIO_LED_4 ) #define GPIO_OUTPUT_MASK ( MASK_GPIO_LED_1 | MASK_GPIO_LED_2 | MASK_GPIO_LED_3 | MASK_GPIO_LED_4 ) #define GPIO_INPUT_MASK ( MASK_GPIO_LED_1 | MASK_GPIO_LED_2 | MASK_GPIO_LED_3 | MASK_GPIO_LED_4 ) #endif // _GPIODEV_HEADER_ 직접 비트 값을 계산해 보면 알겠지만, GPIO_OUTPUT_MASK 와 GPIO_INPUT_MASK 값은 1110 으로 같다. ===== 2. gpio.c ===== 가장 핵심적인 내용이라고 할 수 있는 디바이스 파일이다. //------------------------------------------------------------------------------ // 화일명 : gpio.c // 프로젝트 : GPIO 디바이스 드라이브 제작 // 설 명 : GPIO의 디바이스 드라이브 소스이다. // // 작성자 : 장형기 (주)제이닷디엔티 tsheaven@falinux.com // 작성일 : 2003년 06월 30일 (월) // 저작권 : (주)제이닷디앤티 // 주 의 : // 1. 이것은 EZ-X5보드용 LED ON/OFF용 GPIO디바이스 드라이버를 시험하기 // 위한 프로그램 이다. // // 2. EZ-X5보드의 DEBUG LED를 이용하여 프로그램하였다. // // 3. 사용된 GPIO는 GP2, GP3, GP4, GP5 번이다. // //------------------------------------------------------------------------------ //****************************************************************************** // // 헤더 정의 // //****************************************************************************** #ifndef __KERNEL__ # define __KERNEL__ #endif #ifndef MODULE # define MODULE #endif #include #include #include // printk() #include #include // kmalloc() #include // everything... #include // error codes #include // size_t #include #include // O_ACCMODE #include #include #include #include #include #include #include #include #include #include #include #include #include #include // cli(), *_flags #include #include #include #include #include #include "gpio.h" /* Set of debugging defines */ #define GPIO_DEBUG #undef GPIO_DEBUG_NO DECLARE_WAIT_QUEUE_HEAD(gpio_wait); static int gpio_outb( int data ); static int gpio_inb ( void ); // 디바이스 오픈 상태 : [ 1이면 사용 / 0이면 미사용 상태 ] static int gpio_usage = 0; //****************************************************************************** // // 함수 정의 // //****************************************************************************** //------------------------------------------------------------------------ // 설 명 : IO 를 초기화를 설정한다. // 매 계 : // 반 환 : // 주 의 : // 1. include/asm/arch-pxa/pxa-regs.h 에 정의 되어 있음 // // 2. 레지스터의 설정 값에 대한 정의 설명은 다음 사이트의 강좌를 참조 // http://www.falinux.com [ 하드웨어 >> XScale [PXA250] GPIO ] // //------------------------------------------------------------------------ void GPIO_IO_Init( void ) { // 출력 정의 (모두 비트를 0 으로 세팅) GAFR0_L &= ~( GPIO_OUTPUT_MASK ); // Disable Alternative Function 부가기능 비활성화 GRER0 &= ~( GPIO_OUTPUT_MASK ); // Clear Rising edge trigger. 엣지 검출 기능 비활성화 GFER0 &= ~( GPIO_OUTPUT_MASK ); // Set as Falling Edge Detect 엣지 검출 기능 비활성화 } //------------------------------------------------------------------------ // 설 명 : GPIO 출력 // 매 계 : 없음 // 반 환 : 없음 // 주 의 : // 참 고 : //------------------------------------------------------------------------ int gpio_outb( int data ) { // 출력 전용으로 설정 (모두 비트 1로 세팅) GPDR0 |= ( GPIO_OUTPUT_MASK ); //GPSR은 출력 SET 레지스터 GPSR0 |= ( GPIO_OUTPUT_MASK ); //GPCR은 출력 Clesar 레지스터 GPCR0 = GPCR0 | (data << 2); return 0; } //------------------------------------------------------------------------ // 설 명 : GPIO 입력 처리 [전체 GPIO 입력 상태를 리턴] // 매 계 : 없음 // 반 환 : 없음 // 주 의 : // 참 고 : //------------------------------------------------------------------------ int gpio_inb( void ) { // 입력 전용으로 설정 GPDR0 &= ~( GPIO_INPUT_MASK ); return ~( GPLR0 >> 2 ); } //------------------------------------------------------------------------ // 설 명 : read // 매 계 : 없음 // 반 환 : 없음 // 주 의 : //------------------------------------------------------------------------ ssize_t gpio_read (struct file *filp, char *buf, size_t count, loff_t *f_pos) { unsigned long data=0; // GPIO의 출력 상태값을 읽어 온다. data = ( unsigned long )gpio_inb(); put_user( data, buf ); return count; } //------------------------------------------------------------------------ // 설 명 : write // 매 계 : 없음 // 반 환 : 없음 // 주 의 : // 참 고 : //------------------------------------------------------------------------ ssize_t gpio_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos ) { const unsigned char *gpiodata = buf; int data=0; get_user( data, gpiodata ); gpio_outb( data ); // 사용된 길이만큼 돌려 준다. return count; } //------------------------------------------------------------------------ // 설 명 : open // 매 계 : 없음 // 반 환 : 없음 // 주 의 : //------------------------------------------------------------------------ int gpio_open(struct inode *inode, struct file *filp) { // 이미 사용중이면 사용중이라는 값을 되돌린다. if( gpio_usage != 0 ) return -EBUSY; MOD_INC_USE_COUNT; // 사용수 카운트를 증가 시킨다. gpio_usage = 1; // 디바이스 사용중. return 0; } //------------------------------------------------------------------------ // 설 명 : release // 매 계 : 없음 // 반 환 : 없음 // 주 의 : //------------------------------------------------------------------------ int gpio_release(struct inode *inode, struct file *filp) { MOD_DEC_USE_COUNT; // 사용수 카운트를 감소 시킨다. gpio_usage = 0; // 사용하지 않음을 표시 return 0; } //------------------------------------------------------------------------ // 설 명 : file_operations 구조체 // 매 계 : 없음 // 반 환 : 없음 // 주 의 : //------------------------------------------------------------------------ static struct file_operations gpio_fops = { read : gpio_read, write : gpio_write, open : gpio_open, release : gpio_release, }; //------------------------------------------------------------------------ // 설 명 : 모듈 생성 // 매 계 : 없음 // 반 환 : 없음 // 주 의 : // 참 조 : // include/asm/arch/irqs.h 에 GPIO핀 관련 정의가 있음.. // // register_chrdev( 장치 메이저 번호, // 장치 명, // 장치 접근 함수 구조체 주소 ); //------------------------------------------------------------------------ static int __init gpio_init_module (void) { int result; // 장치를 등록한다. result = register_chrdev( GPIO_MAJOR, DEVICE_NAME, &gpio_fops ); if (result < 0) { printk(KERN_WARNING "\n%s : Can't get Major Number [%d]\n", DEVICE_NAME, GPIO_MAJOR); return result; } printk("\n\n\nInit madule, Succeed. This Device is %s and Major Number is [%d]\n\n", DEVICE_NAME, GPIO_MAJOR); // GPIO 제어를 위한 GPIO 초기화 GPIO_IO_Init(); return 0; /* 성공 */ } //------------------------------------------------------------------------ // 설 명 : 모듈 소멸 // 매 계 : 없음 // 반 환 : 없음 // 주 의 : // 참 조 : // unregister_chrdev( 장치 메이저 번호, // 장치 명 ); // //------------------------------------------------------------------------ static void gpio_cleanup_module (void) { // 모듈을 해제한다.. if ( !unregister_chrdev( GPIO_MAJOR, DEVICE_NAME) ) printk("%s Device Exit Sucess...\n", DEVICE_NAME); else printk("%s Device Exit Fail...\n", DEVICE_NAME); } module_init(gpio_init_module); module_exit(gpio_cleanup_module); 이지보드에서 제공하는 소스파일을 그대로 옮겼으므로, 별도의 설명은 필요가 없을 것 같다. 초반에 어떻게 비트를 세팅해주는지 확인해보자! 그리고 또한 리눅스에서 디바이스드라이버를 만들 때, 공통적으로 적용되는 형식에 대해서도 살펴보기 바란다. 나중에 컴파일 후에 모듈로 커널에 로딩할 것이다. ===== 3. test.c ===== 모듈이 올라간 상태에서 테스트 프로그램을 통해, GPIO 를 제어하는 프로그램이다. int main(int argc, char **argv) { int dev, lp; char buff[256]; // 화일을 연다. 프로그램 수행전에 mknod 를 이용해서 /dev/gpio 파일을 생성해야 한다. dev = open("/dev/gpio", O_RDWR|O_NDELAY ); if (dev < 0) { printf( "GPIO Open Fail\n"); // 화일 열기 실패 exit(1); } printf( "\nGPIO Open Success\n\n" ); memset( buff, 0 , sizeof(buff) ); for( lp = 0; lp < 16; lp++ ) { buff[0] = lp ; write( dev, buff, 1 ); // LED로 출력한다. usleep( 500000 ); read( dev, buff, 256 ); // LED를 읽는다. printf( "Read LED...[0x%02X]\n", buff[0]& 0x0F ); } close(dev); printf( "\nGPIO Process Ending\n\n" ); return 0; } 생각보다는 간단한 프로그램이다. ====== 컴파일 및 실행 ====== 앞에서도 얘기했지만, 크로스컴파일러 환경이 구축되어야 한다. 또한 이지보드에서 제공한 소스이기 때문에 Makefile 을 제공한다. #make clean #make dep #make 에러없이 컴파일 되었다면, device 디렉토리 아래와 app 디렉토리 아래에 오브젝트 파일이 생성되었을 것이다. 이 2 개의 파일을 타겟보드로 다운로드하자! #insmod gpio_dev.o #lsmod #mknod /dev/gpio c 253 0 #./test_app 어떤가? LED 가 깜박 거리지 않는가?? ---- {{indexmenu>:#1|skipns=/^(wiki|etc|diary|playground)$/ skipfile=/^(todays|about|guestbook)$/ nsort rsort}} ----