【问题标题】:Display "Hello World" on framebuffer in linux在 linux 的帧缓冲区上显示“Hello World”
【发布时间】:2018-05-28 09:06:06
【问题描述】:

我在我的 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
  • 当您运行代码时,您是从裸帧缓冲区终端运行它,还是从通过操作系统的 GUI 启动的终端仿真器运行它?

标签: linux framebuffer


【解决方案1】:

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

他们提供了fbutils.h,您可以在fbutils-linux.c 中找到其实现。这段代码非常简单,直接操作linux framebuffer,没有任何依赖。目前还不到 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 帧缓冲区,首先您应该将其内存映射到您的进程地址空间。内存映射后,您可以像访问数组一样访问它。使用ioctl,您可以获得有关帧缓冲区的信息,例如分辨率、每像素字节数等。有关详细信息,请参阅here。 在下面的代码中,可以传入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 函数来访问设备。这些函数处理可能很麻烦的内部缓冲区。更喜欢打开和编写函数。

    接下来,您不能继续使用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));
    

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

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

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

    字体是单色的,所以每一位代表一个像素。在您的帧缓冲区中,一个像素需要 4 个字节。所以你必须做一些转换。

    这是访问帧缓冲区的一种非常基本的方法,还有很多改进要做:

    • 应使用FBIO*ET_*SCREENINFO ioctl 协商/检索列、行和像素表示。
    • 使用write 访问帧缓冲区不是首选方法。它很慢,并且不允许轻松更新帧缓冲区。首选方法使用mmap
    • 如果你想为帧缓冲区设置动画,你可以使用双缓冲区:分配一个比需要大两倍的缓冲区,交替写入第一部分或第二部分,并使用FBIOPAN_DISPLAY 更新显示的缓冲区
    • font8x8 并不理想。您可能想使用网络上可用的任何其他字体。您需要一个库来解码字体格式 (libfreetype) 和一个库来将特定大小的字形(= 字母)渲染到缓冲区(也称为 rasterize 步骤),您可以将其复制到您的屏幕(libpango)
    • 您可能希望加速字形数据库和屏幕帧缓冲区之间的缓冲区复制(又名 compose 步骤),但涉及真正的 GPU 驱动程序的故事要长得多

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-27
      • 1970-01-01
      • 2012-12-25
      相关资源
      最近更新 更多