필자는 예전에 리눅스 강의할 때 프레임버퍼를 강의할 때면 점, 선, 면... 순으로 실습을 진행했었던 기억이 있다. 그때는 임베디드 장비에 LCD가 연결되어있고, 해상도가 480*272 또는 640 * 480 정도 되는 상대적으로 크기가 큰 LCD를 주로 이용했었는데, 요즘에 그정도 LCD는 잘 쓰지 않는다. 아니 오픈소스 하드웨어 기반의 장치들은 대부분 HDMI 포트를 제공해주는 것이 대부분이라 보통 해상도가 1920*1080 정도가 된다. 그러다보니 점(dot)을 그리는 명령 예제를 실행하면 점을 찾는다고 고생을 해야된다. 잘 안보여서 말이다. 그래서 점은 설명으로 대체하도록 하겠다.

     

      우리가 앞서서 BPP를 확인했던 적이 있다. 기억이 나지 않는 독자를 위해서 다시 결과 화면을 가지고 왔다. 아래를 보면 BPP가 16이라고 되어있다. 다시말해 화면에 픽셀(점)하나를 그리기 위해서 16비트가 필요하다는 이야기다. 16비트는 2바이트인데, 색상을 표현하기 위해서는 R,G,B의 정보가 있어야 된다. 보통 우리는 윈도우 기반에서 프로그래밍을 할때 RGB888(또는 RGB24 라고도 함)을 이용했는데, 프레임버퍼에서 이용할 경우 16비트로 처리해야 되므로  RGB565형식을 사용해야 된다. 그건 알겠고, 색상 순서는 BGR순서로 되어있다는 것을 결과에서 확인할 수 있다. 보면 비트 옵셋이 Blue가 0, Green이 5, Red가 11로 표시되었다. 따라서 2바이트로 출력할 수 있는 16비트의 하위 5바이트는 Blue, 그다음 6비트는  Green, 마지막 5비트는 Red임을 확인할 수 있다. 

     

     

    1. 프레임버퍼 전체 채우기 (fb_fill.c)

      프레임버퍼의 정보를 확인할 수 있었으니 해당 전체에 적색을 표시하려고 한다. 그러면 메모리는 얼마만큼 필요할까? 반복문을 통해서 장치에 포인터로 직접 기록하는 경우에는 버퍼를 직접 만들 필요는 없겠지만, 필요에 의해서(보통 오버레이 기능구현이나 먹싱등) 한번에 프레임버퍼에 기록해야되는 경우가 있는데 그런경우, 프레임버퍼 만큼 배열을 만들어서 사용한다. 해상도가 1920 * 1080 이고 BPP가 16이니 바이트로는 2바이트 따라서 

     

      1920 * 1080 * 2 = 4,147,200 Byte 따라서, 약 4MByte 정도의 메모리가 필요하다. 

     

     

    2. 소스입력 (fb_fill.c)

      다음과 같이 소스를 작성할 한다. 소스에 대해서는 주석으로 설명을 한다. 만약, 추가적인 설명이 필요하다면 댓글로 작성하면 시간나는데로 답을 하도록 하겠다. 

     

    // File : fb_fill.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include <sys/mman.h>
    #include <linux/fb.h>

    #define FBDEV "/dev/fb0"

    // RGB 데이터를 받아서 16비트(2바이트)로 변경하기 위한 함수
    unsigned short rgb24to16(unsigned char r, unsigned char g, unsigned char b)
    {
        return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
    }

    void usage(char* str)  // 잘못된 인자를 받았을때 출력할 예시
    {
        printf("Usage: %s <color>\n", str);
        printf("  Color - 'r'ed, 'g'reen, 'b'lue\n");
        printf("  ex) %s r\n",str);
    }

    int main(int argc, char **argv)
    {
        int fd; // 프레임버퍼용 파일 디스크립트
        char color;
        unsigned char red=0,green=0,blue=0;  // 생상값을 지정할 변수
        unsigned short pixel;   // 픽셀 색상을 위한 변수
        int pixel_offset;       // 픽셀의 위치를 위한 변수

        if (argc != 2)  {  //  프로그램 실행할 때 인자는 반드시 1개가 포함이되어야 한다.
            usage(argv[0]);
            return -1;
        }

        color = argv[1][0]; // 문자열로 넘어오는 인자의 첫번째 바이트만 가져온다

        switch (color) {
            case 'r':
                red = 255;
                break;
            case 'g':
                green = 255;
                break;
            case 'b':
                blue = 255;
                break;
            default:
                printf("Invalid color");
                usage(argv[0]);
                return -1;
        }


        // Open framebuffer device
        fd = open("/dev/fb0", O_RDWR);
        if (fd < 0) {
            printf("Device %s open error\n",FBDEV);
            return -1;
        }

        // 프레임버퍼 정보 가져오기
        struct fb_var_screeninfo scr_info;
        if (ioctl(fd, FBIOGET_VSCREENINFO, &scr_info) < 0) {
            close(fd);
            return -1;
        }

        // 메모리 매핑을 위해서 프레임버프의 크기를 담는다. *2는 앞에서 설명하였다. 2바이트라고..
        size_t fb_size = scr_info.yres_virtual * scr_info.xres_virtual * 2;

        // 보이드 포인터 fb_ptr로 프레임버퍼를 매핑한다.
        void *fb_ptr = mmap(NULL, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (fb_ptr == MAP_FAILED) {
            perror("mmap");
            close(fd);
            return -1;
        }

        // 앞서 보이드포인터로 해밍한 데이터를 2바이트 단위로 쓰기 위해서 타입 캐스팅 한다.
        unsigned short *pbuf = (unsigned short *) fb_ptr;

        int width = scr_info.xres_virtual;
        int height = scr_info.yres_virtual;

        // 메모리는 선형 구조이므로, 길다란 작데기 처럼 늘어져 있다. 따라서, x는 가로이므로
        // 1080픽셀 단위로 y가 증가한다. pixel_offset은 그 위치를 계산하기 위한 변수이다.
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                pixel_offset = y * width + x;

                // 픽셀 색상을 만든다. RGB값을 넣어서 2바이트 데이터를 pixel 변수에 담는다.
                pixel = rgb24to16(red, green, blue);

                // 프레임버퍼에 만들어진 색상 값을 넣는다.
                pbuf[pixel_offset] = pixel;
            }
        }

        // 매핑했던 메모리를 반환한다.
        munmap(fb_ptr, fb_size);

        // 열었던 프레임 버퍼를 닫는다.
        close(fd);

        return 0;
    }

     

        

    3. 컴파일

      컴파일 하는 방법은 앞의 fb_info 와 동일하다. 

      

      # gcc -o fb_fill fb_fill.c

     

     

    4. 결과 확인

      결과는 아래의 그림과 같이 명령을 입력하여 모니터를 지정한 색상으로 채울 수 있다.

     

      $ ./fb_fill red 또는 ./fb_fill r

     

    필자는 예제로 red라고 입력하여 빨간색으로 채워 보았다. 나이가 드니까 이런색이 나쁘지 않게 느껴진다.. 에혀.. 그리고 위에 검은색 부분은 커서 부분이다. 리눅스 시스템에서 설정할 수 있다. 고정이나 블링크 또는 언더바만 나오는등의 설정을 해줄수 있는데, 디폴트가 깜빡이는 커서이고, 앞의 경로명등은 한번 뿌려지고 갱신되지 않으므로, 커서 부분만 저렇게 검정색으로 표시된다. 

     

      나중에 여러분들이 프레임버퍼를 이용해서 GUI를 설계하고자 하는 일이 발생한다면, 커널 옵션이나 시스템 설정에서 해당 부분을 설정해주면 깔금하게 깔끔하게 표시할 수 있다. 

     

      별도로 프레임버퍼를 초기화 하는 기능을 만들지 않은경우, 리눅스 터미널에서  clear 명령을 통해서 지울수 있다. 다음에는 선, 사각형, 원을 그리게 될테인데, 기본적인 소스 설명은 생략하거나 더 간단하게 진행하고, 마지막으로 이전에 bmp  파일을 읽어와서 화면에 뿌리는 것으로 프레임버퍼 다루기는 마무리 지을 생각이다. 

     

     

     

      

    반응형
    • 네이버 블러그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기