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 <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> // printk() #include <linux/version.h> #include <linux/slab.h> // kmalloc() #include <linux/fs.h> // everything... #include <linux/errno.h> // error codes #include <linux/types.h> // size_t #include <linux/proc_fs.h> #include <linux/fcntl.h> // O_ACCMODE #include <linux/ioport.h> #include <linux/vmalloc.h> #include <linux/ioctl.h> #include <linux/sched.h> #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/major.h> #include <linux/delay.h> #include <linux/string.h> #include <linux/signal.h> #include <linux/ioport.h> #include <linux/mm.h> #include <linux/init.h> #include <asm/system.h> // cli(), *_flags #include <asm/irq.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/hardware.h> #include <asm/arch-pxa/pxa-regs.h> #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 가 깜박 거리지 않는가??