在Linux上在帧缓冲区中显示“Hello World”

4

我在我的ARM目标上使用了Linux 3.14版本,并希望使用帧缓冲器在显示器上显示一些字符行。我可以使用以下代码更改显示器的颜色。

#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;
}

同样地,我想在显示器上展示一些字符行。例如,我想使用帧缓冲在显示器上展示“Hello world !!!”这些字符。
请问有谁可以帮我解决这个问题?

你在将哪个设备名称作为参数传递给 main 函数? - Woodrow Barlow
当你运行代码时,是从裸的帧缓冲终端还是从通过操作系统 GUI 启动的终端模拟器中运行它? - Woodrow Barlow
2个回答

6

您可以在tslib中找到一个优雅的代码片段来完成此操作。 tslib是一个用于过滤触摸屏事件的C库。实际上,您不需要tslib来达到您的目的(是的,您不必构建它)。在他们的tests中,您可以找到一个访问帧缓冲区的实用程序。

他们提供了fbutils.h,其实现可以在fbutils-linux.c中找到。这个代码非常简单,因为它直接操作Linux帧缓冲区,没有任何依赖关系。目前它甚至不到500行,如果您只想显示文本,可以删除其他无关功能。它支持两种字体-font_8x8font_8x16,您可以在各自的.c文件中找到它们的定义。

我不会详细介绍代码,因为它很容易理解。只会列出当前的API并提供打开和关闭功能的更简单的代码。

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);

要操作Linux framebuffer,首先需要将其内存映射到您的进程地址空间中。映射内存后,您可以像访问数组一样访问它。使用一些ioctl,您可以获取有关framebuffer的信息,例如分辨率、每像素字节数等。有关详细信息,请参见此处

在下面的代码中,您可以传递fb设备的名称以打开它,例如/dev/fb0。您可以使用原始代码中的其余函数进行绘制。

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;
}

你可以在文件夹中的测试程序中找到使用它的示例,例如ts_test.c
你可以扩展此代码以支持其他字体、显示图像等。
祝你好运!

2
首先,我强烈建议避免使用fopen/fwrite函数来访问设备。这些函数处理内部缓冲区可能会出现问题。最好使用open和write函数。
其次,你不能继续使用一系列的 if..then..else 来呈现真正的图形。你需要分配一个表示帧缓冲区的缓冲区。它的大小将是 columns * lines * 4(每种基本颜色都需要1个字节)。要写入像素,你必须使用类似下面的代码:
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;

一旦您的缓冲区完全填充,请使用以下方式写入:

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

(其中fd是通过fd = open("/dev/fbdev0", O_WRONLY);返回的文件描述符)

检查您是否能够在我们的帧缓冲上设置任意像素。

最后,您需要一个已渲染字符的数据库。您可以自己创建它,但我建议使用https://github.com/dhepper/font8x8

字体是单色的,因此每个比特表示一个像素。在您的帧缓冲上,一个像素需要4个字节。因此,您需要进行一些转换。

这是访问帧缓冲的基本方法,还有很多改进的空间:

  • 列、行和像素表示应该使用FBIO*ET_*SCREENINFO ioctl进行协商/检索。
  • 使用write访问帧缓冲不是首选方法。它很慢,而且不容易更新帧缓冲。首选方法使用mmap
  • 如果您想要对帧缓冲进行动画处理,则需要使用双缓冲:分配一个比所需空间大两倍的缓冲区,交替写入第一部分或第二部分,并使用FBIOPAN_DISPLAY更新显示的缓冲区。
  • font8x8不是理想的选择。您可能希望使用Web上提供的任何其他字体。您需要一个解码字体格式的库(libfreetype)和一个将字形(=字母)以特定大小呈现到缓冲区中(即光栅化步骤)的库,然后可以将其复制到屏幕上(libpango)
  • 您可能希望加速从字形数据库到屏幕帧缓冲区之间的缓冲区复制(即组合步骤),但这是一个更长的故事,涉及真正的GPU驱动程序

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接