====== 이지보드-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}}
----