Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Display "Hello World" on framebuffer in linux

I have used the linux 3.14 version on my ARM target and i want to show some line of characters in the display using frame buffer. I can change the colors of the display using the below code.

#include <stdio.h>

unsigned char colours[8][4] = {
    { 0x00, 0xFF, 0x00, 0xFF }, // green
    { 0x00, 0xFF, 0x00, 0xFF }, // green
    { 0x00, 0xFF, 0x00, 0xFF }, // green
    { 0x00, 0xFF, 0x00, 0xFF }, // green
    { 0x00, 0xFF, 0x00, 0xFF }, // green
    { 0x00, 0xFF, 0x00, 0xFF }, // green
    { 0x00, 0xFF, 0x00, 0xFF }, // green
    { 0x00, 0xFF, 0x00, 0xFF }, // green
};

int frames[] = {0,5,10,15,20,25,30};

int columns = 800;
int lines = 480;

#define ARRAY_SIZE(a)   (sizeof(a)/sizeof(a[0]))        

int frame(int c, int l){
    int i;
    for(i=0; i < ARRAY_SIZE(frames); i++){
        if((c==frames[i])&&((l>=frames[i])&&l<=(lines-frames[i]))){
            return 1;
        }
        if((c==columns-frames[i])&&((l>=frames[i])&&l<=(lines-frames[i]))){
            return 1;
        }
        if((l==frames[i])&&((c>=frames[i])&&c<=(columns-frames[i]))){
            return 1;
        }
        if((l==lines-frames[i])&&((c>=frames[i])&&c<=(columns-frames[i]))){
            return 1;
        }
    }
    return 0;
}

int main(int argc, char **argv)
{
    unsigned char pixel[3];
    int l, c;
    char *filename = argv[1];       
    printf ("Device : %s\n",filename);
    FILE *f = fopen(filename,"wb");

    if(f){
    printf("Device open success \n");
        for(l=0; l<lines; l++){
            for(c=0; c < columns; c++){
                if(frame(c,l)){
                    fwrite(colours[3], 1, sizeof(colours[3]), f);
                }else{
                    int colour = c/(columns/ARRAY_SIZE(colours)); 
                    fwrite(colours[colour], 1, sizeof(colours[colour]), f);
                }
            }
        }
        fclose(f);
    }
    else
        printf("Device open failed \n");

    return 0;
}

In the same way i want to show some lines of character to the display. Example, I want to show characters "Hello world !!!" in the display using frame buffer.

Could any one help me to work it out.

like image 427
Selva Kumar Avatar asked Jan 29 '23 01:01

Selva Kumar


2 Answers

You can find an elegant piece of code to do this in tslib. tslib is a c library for filtering touchscreen events. Actually, you don't need tslib for your purpose (yes, you don't have to build it). In their tests you can find a utility to access the framebuffer.

They have provided the fbutils.h whose implementation you can find in fbutils-linux.c. This code is very simple in that it directly manipulates the linux framebuffer and does not have any dependencies. Currently it's not even 500 lines, and if you only want to display text, you can remove other irrelevant functionality. It supports two fonts - font_8x8 and font_8x16 - whose definitions you can find in the respective .c files.

I won't go into code details as it is easy to understand. Will just list the current API and provide a simpler code for open and close functionality.

int open_framebuffer(void);
void close_framebuffer(void);
void setcolor(unsigned colidx, unsigned value);
void put_cross(int x, int y, unsigned colidx);
void put_string(int x, int y, char *s, unsigned colidx);
void put_string_center(int x, int y, char *s, unsigned colidx);
void pixel(int x, int y, unsigned colidx);
void line(int x1, int y1, int x2, int y2, unsigned colidx);
void rect(int x1, int y1, int x2, int y2, unsigned colidx);
void fillrect(int x1, int y1, int x2, int y2, unsigned colidx);

To manipulate the linux framebuffer, first you should memory map it into your process address space. After memory mapping you can access it just like an array. Using some ioctl you can get information about the framebuffer such as resolution, bytes-per-pixel etc. See here for details. In the code below, you can pass the name of the fb device to open it, such as /dev/fb0. You can use the rest of the functions in the original code for drawing.

int open_framebuffer(const char *fbdevice)
{
    uint32_t y, addr;

    fb_fd = open(fbdevice, O_RDWR);
    if (fb_fd == -1) {
        perror("open fbdevice");
        return -1;
    }

    if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &fix) < 0) {
        perror("ioctl FBIOGET_FSCREENINFO");
        close(fb_fd);
        return -1;
    }

    if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &var) < 0) {
        perror("ioctl FBIOGET_VSCREENINFO");
        close(fb_fd);
        return -1;
    }

    xres_orig = var.xres;
    yres_orig = var.yres;

    if (rotation & 1) {
        /* 1 or 3 */
        y = var.yres;
        yres = var.xres;
        xres = y;
    } else {
        /* 0 or 2 */
        xres = var.xres;
        yres = var.yres;
    }

    fbuffer = mmap(NULL,
               fix.smem_len,
               PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED,
               fb_fd,
               0);

    if (fbuffer == (unsigned char *)-1) {
        perror("mmap framebuffer");
        close(fb_fd);
        return -1;
    }
    memset(fbuffer, 0, fix.smem_len);

    bytes_per_pixel = (var.bits_per_pixel + 7) / 8;
    transp_mask = ((1 << var.transp.length) - 1) <<
        var.transp.offset; /* transp.length unlikely > 32 */
    line_addr = malloc(sizeof(*line_addr) * var.yres_virtual);
    addr = 0;
    for (y = 0; y < var.yres_virtual; y++, addr += fix.line_length)
        line_addr[y] = fbuffer + addr;

    return 0;
}

void close_framebuffer(void)
{
    memset(fbuffer, 0, fix.smem_len);
    munmap(fbuffer, fix.smem_len);
    close(fb_fd);

    free(line_addr);

    xres = 0;
    yres = 0;
    rotation = 0;
}

You can find examples of its usage in test programs in the folder, such as ts_test.c.

You can extend this code to support other fonts, display images etc.

Good luck!

like image 140
dhanushka Avatar answered Jan 31 '23 20:01

dhanushka


First, I strongly suggest to avoid use of fopen/fwrite function to access devices. These function handle internal buffers that can be troublesome. Prefers functions open and write.

Next, you can't continue with series of if .. then .. else .. to render a true graphic. You need to allocate a buffer that represent your framebuffer. Its size will, be columns * lines * 4 (you need 1 byte per primary color). To write a pixel, you have to use something like:

buf[l * columns + c * 4 + 0] = red_value;
buf[l * columns + c * 4 + 1] = green_value;
buf[l * columns + c * 4 + 2] = blue_value;
buf[l * columns + c * 4 + 3] = alpha_value;

Once you buffer is fully filled, write it with:

write(fd, buf, sizeof(buf));

(where fd is file descriptor return by fd = open("/dev/fbdev0", O_WRONLY);)

Check that you are now able to set arbitrary pixels on our framebuffer.

Finally, you need a database of rendered characters. You could create it yourself, but I suggest to use https://github.com/dhepper/font8x8.

Fonts are monochrome so each bit represent one pixel. On your framebuffer, you need 4bytes for one pixel. So you will have to do some conversion.

This is a really basic way to access framebuffer, there are plenty of improvements to do:

  • columns, lines and pixel representation should negotiated/retrieved using FBIO*ET_*SCREENINFO ioctl.
  • using write to access framebuffer is not the preferred method. It is slow and does not allow to updating framebuffer easily. The preferred method use mmap.
  • if you want to to animate framebuffer, you to use a double buffer: allocate a buffer twice larger than necessary, write alternatively first part or second part and update shown buffer with FBIOPAN_DISPLAY
  • font8x8 is not ideal. You may want to use any other font available on web. You need a library to decode font format (libfreetype) and a library to render a glyph (= a letter) in a particular size to a buffer (aka rasterize step) that you can copy to your screen (libpango)
  • you may want to accelerate buffer copy between your glyph database and your screen framebuffer (aka compose step), but it is a far longer story that involve true GPU drivers
like image 36
Jérôme Pouiller Avatar answered Jan 31 '23 20:01

Jérôme Pouiller