응용프로그램에서 하드웨어 레지스터 접근하기
===========================================

1. 개요

이 문서는 응용프로그램에서 mmap 를 이용하여 하드웨어를제어하는
방법에 대한 문서이다.

이 문서에 설명한 것은 EZ-X5를 이용하여 시험하였다.

2. 설명

임베디드용 하드웨어를 리눅스 커널에서 시험할때 보통 디바이스 드라이버를 제작하여
시험하게 된다.

그런데 이 과정은 솔찍히 무척 불편하고 디바이스 드라이버를 작성할만한 능력이
없는 사람들에게는 무척 곤란해 하는 부분이다.

그렇다면 방법이 없을까?

리눅스를 만든 사람들은 이런것을 그냥 두지 않는다.

대부분의 리눅스에서는 두가지 장치파일이 있다.

/dev/mem - 메모리 장치파일
/dev/ioport - IO 장치파일

이 두 장치파일중 /dev/mem 은 거의 필수적으로 갖추어져 있다.

/dev/mem은 시스템의 메모리 공간에 접근할수 있는 장치파일이다.
이 장치파일을 이용하면 EZ-X5에 사용되는 ARM 계열의 프로세서는
IO 레지스터에 접근할수 있다.

물론 같은 방법으로 PC 에서는 PCI 장치에 접근할 수 있다.

/dev/ioport 는 PC에서 사용하는 장치파일로 PC의 in,out 명령으로접근하는
메모리 공간에 접근할 수 있다.

여기서는 /dev/mem에 대해서만 이야기 하겠다.

또 한가지 /dev/mem 장치파일을 이용해서 특정 물리적인 메모리 공간에 접근하는
방법은 두가지 인데

하나는 lseek 를 이용하여 주소를 지정하고 read 또는 write 를 이용하여 접근하는
방법이다.
또다른 방법은 mmap 함수를 이용하여 접근하는 방법이다.

초보 리눅스 프로그래머에게는 lseek,read,write 함수를 이용하는 방법이 더 편하겠지만
버스폭에 대한 처리를 요구하는 경우에는 이 함수들은 제한이 있다.

그래서 하드웨어를 시험하기 위해서는 mmap 함수를 사용하는 것이다.

다음 예제는 mmap 함수를 이용하여 EZ-X5에 있는 D10 번 LED를 점멸하는 예제이다.

D10 LED는 GPIO2 에 연결되어 있다.

GPIO 의 레지스터에 대한 것은

http://www.falinux.com/win/01_hw/030_xscale/gpio.htm

를 참조하거나 PXA255 레퍼런스를 참조 하기 바란다.

3. 예제 소스

#include
#include
#include

#include
#include
#include

#include

#define IO_BASE_ADDR 0x40000000
#define IO_SIZE 0x0C000000

#define IO_GPDR0_ADDR 0x40E0000C
#define IO_GPSR0_ADDR 0x40E00018
#define IO_GPCR0_ADDR 0x40E00024

#define IO_GPDR0_OFFSET ( IO_GPDR0_ADDR - IO_BASE_ADDR )
#define IO_GPSR0_OFFSET ( IO_GPSR0_ADDR - IO_BASE_ADDR )
#define IO_GPCR0_OFFSET ( IO_GPCR0_ADDR - IO_BASE_ADDR )

#define IO_GPIO2_LED (1<<2)

int main( int argc, char **argv )
{
int fd;

void *baseaddr;
unsigned long *gpdr0;
unsigned long *gpsr0;
unsigned long *gpcr0;

fd = open( "/dev/mem", O_RDWR|O_SYNC );
if( fd < 0 )
{
perror( "/dev/mem open error" );
exit(1);
}

// IO 메모리를 얻는다.
baseaddr = mmap( 0, // 커널에서 알아서 할당요청
IO_SIZE, // 할당 크기
PROT_READ|PROT_WRITE, MAP_SHARED, // 할당 속성
fd, // 파일 핸들
IO_BASE_ADDR ); // 매핑 대상의 물리주소

printf( "IO ADDR %p
", baseaddr );

if( baseaddr != NULL )
{
// GPIO 어드레스
gpdr0 = (unsigned long *) ( (unsigned char *)baseaddr + IO_GPDR0_OFFSET );
gpsr0 = (unsigned long *) ( (unsigned char *)baseaddr + IO_GPSR0_OFFSET );
gpcr0 = (unsigned long *) ( (unsigned char *)baseaddr + IO_GPCR0_OFFSET );

printf( "GPDR0 STATE %08X
", *gpdr0 );

// LED 껌복거리기
while(1)
{
*gpsr0 = IO_GPIO2_LED; printf( "LED ON
" );
sleep(1);
*gpcr0 = IO_GPIO2_LED; printf( "LED OFF
" );
sleep(1);
}

if( baseaddr != NULL ) munmap( baseaddr, IO_SIZE ); // 매핑된것 해제
}

close( fd );

return 0;
}