티스토리 뷰

 Raspberry pi의 GPIO는 gpio header를 이용하여 함수로 쉽게 제어할 수 있습니다. 하지만 이번에는 register에 접근하여 GPIO를 조작해서 LED를 깜박여보겠습니다.

앞으로 진행할 과정을 간단히 scenario로 정리하면 아래와 같습니다.

1). Raspberry pi의 memory mapped I/O의 개념을 이해한다.
2). Raspberry pi의 GPIO관련 register를 이해한다.
3). Linux의 memory map을 이해한다.
4). GPIO 조작!

 GPIO를 조작하는데 생각보다 많은 개념들이 필요해보입니다. 이런 개념들을 순차적으로 이해한 후 GPIO를 조작해보겠습니다.


1. memory mapped I/O

 Microprocessor의 GPIO는 register로 관리됩니다. 예를 들어서 사용자가 GPIO를 사용하려면, microprocessor에게 어떤 pin을 사용할 것인지, 그 pin으로 High 또는 Low로 출력할 것인지 알려줘야 합니다. 사용자는 이러한 정보를 GPIO와 관련된 register에 약속된 설정을 합니다. 구체적으로 C언어로 표현하면 다음과 같습니다. register1 = 0x5C. 약속된 설정에 관한 내용은 조금 있다가 살펴보도록 하겠습니다.

 그럼 본론으로 돌아와서 memory mapped I/O를 설명하겠습니다. Memory mapped I/O는 GPIO와 같은 입출력장치를 access할 때, 입출력과 메모리의 주소 공간을 분리하지 않고 하나의 메모리 공간에 취급하여 배치하는 방식을 말합니다.

 구체적으로 설명하면, GPIO를 조작하기위해 설정했던 register의 주소가 커널이나 사용자 주소공간과 하나의 공간에 존재한다는 것입니다.

위의 그림은 BCM2835의 PA(physical Address)와 VA(Virtual Address)가 MMU(Memory Management Unit)으로 mapping되어있는 관계도입니다. 이 관계도를 보면I/O Peripherals가 다른 메모리와 같이 존재하는 것을 확인할 수 있습니다. 

 Memory mapped I/O와 대조적으로 isolated I/O방식이 있습니다. 이 방법은 이름에서 부터 그 의미를 알 수 있을 것 같습니다. 입출력를 메모리와 구분하는 방식입니다.

 이제 우리는 memory mapped I/O 개념을 어떻게 이용할 수 있을까요? Code를 만들 때, 우리가 원하는 register의 주소에 접근하여 원하는 설정을 해주면, GPIO를 제어할 수 있을 것 같습니다.

 그럼 다음 순서는 GPIO와 관련한 register의 주소를 알아보고 설정을 어떻게 하면되는지 알아보겠습니다.


2. GPIO관련 register

 GPIO와 관련한 register는 BCM2835 datasheet를 통해서 확인할 수 있습니다. 하지만 우리가 사용하는 raspberry pi2의 BCM2836입니다.우리가 사용할 GPIO부분의 내용은 BCM2835과 거의 같습니다. 다만 주소에 변경이 있습니다. 우선 GPIO 설정과 관련한 register를 보겠습니다.

GPFSELx : GPIO pin을 input또는 Output으로 선택
GPSETx : GPIO pin을 High
GPCLRx : GPIO pin을 Low

 위 그림의 resister view를 통해서 register설정과 주소를 알 수 있습니다. 위의 주소는 BCM2385에 해당하는 내용으로 BCM2386에서는 0x7E20 0000을 0x3F20 0000으로 모두 바꾸어서 보면 됩니다.

 우리가 사용할 GPIO는 18번입니다. 해당 핀은 register1에 해당합니다. 이것은 아래 그림을 보면 알 수 있습니다.


3. 리눅스의 Memory Map(mmap)

 이제부터는 실제로 적용하는 과정입니다. 앞선 과정에서 알아낸 주소에 접근을 해야합니다. 그런데 우리가 앞에서 알아본 register의 주소들은 모두 물리 주소입니다. 이 물리 주소에 접근하기 위해서 우리는 /dev/mem 장치를 이용할 것입니다. /dev/mem은 물리 메모리를 컨트롤하는 장치입니다. 그리고 /dev/mem에 읽고 쓰는 것은 memory map을 이용할 것입니다. 

 Memory map을 간략하게 소개하면, 메모리의 내용을 파일이나 디바이스에 대응(mapping)하기 위해서 사용하는 시스템 호출입니다. 쉽게 설명하면, 메모리와 파일을 대응 시키면 메모리를 통해서 파일을 읽을 수도 쓸 수도 있다는 것입니다. 이 기능은 몇 가지 장점을 갖습니다. 첫 번째는 파일은 운영체제의 전역적인 자원이므로 memory map을 이용해서 프로세스간 통신(IPC)를 구현할 수 있습니다. 두 번째, 파일에 읽고 쓰는 것보다 메모리에 읽고 쓰는 것이 더 빠르므로 성능의 향상을 가져옵니다.
 구체적인 설명과 예제의 링크를 걸어두겠습니다.
http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/system_programing/IPC/memory_map


4.GPIO 조작

우리는 LED를 조작하기 위한 충분한 지식을 알게 되었습니다. 이 지식을 이용해서 GPIO18번에 연결된 LED를 5번 깜빡이는 예제는 아래와 같습니다.


#include <stdlib.h>    

#include <stdio.h>    

#include <sys/types.h>    

#include <sys/stat.h>    

#include <fcntl.h>    

#include <sys/mman.h>    


#define GPIO_BASE 0x3F200000    

#define GPFSEL1   0x04  

#define GPSET0    0x1C  

#define GPCLR0    0x28    


int main()    

{    

    int fd = open( "/dev/mem", O_RDWR|O_SYNC );    

    if ( fd < 0 ){    

        printf( "can't open /dev/mem \n" );    

        exit(-1);    

    }    

    char *gpio_memory_map = (char *)mmap( 0, 4096, PROT_READ|PROT_WRITE,    

        MAP_SHARED, fd, GPIO_BASE );    

    if ( gpio_memory_map == MAP_FAILED )    

    {    

        printf( " Error : mmap \n" );    

        exit(-1);    

    }      

    volatile unsigned int* gpio = (volatile unsigned int*)gpio_memory_map;    

    //gpio[GPFSEL1/4] = 0b00000001000000000000000000000000;    

    gpio[GPFSEL1/4] |= (1<<24);    


    int i;    

    for ( i=0; i<5; i++ )    

    {    

        //gpio[GPSET0/4] = 0b00000000000001000000000000000000;    

        gpio[GPSET0/4] |= (1<<18);   

        sleep(1);    

        

        //gpio[GPCLR0/4] = 0b00000000000001000000000000000000;   

        gpio[GPCLR0/4] |= (1<<18);  

        sleep(1);    

    }    

     munmap( gpio_memory_map, 4096);    

    return 0;    

}    



'Embedded' 카테고리의 다른 글

[라즈베리파이2 opencv 설치]  (1) 2015.11.08
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함