MENU

树莓派显示屏

2024 年 09 月 17 日 • 嵌入式

因为目前我使用的都是树莓派zero、2B这些,所以显示屏主要连接有SPI和HDMI。
为什么会有2种,这个主要源于自己的无知。最早买显示屏的时候,啥也不知道,所以就买了一个SPI显示屏。结果发现帧率跟不上。因为SPI的时钟频率最高50MHZ,驱动一个320 * 480的屏幕最高也就10帧左右,实际上无法满足游戏的帧率刷新要求的。

后面又买了一个HDMI驱动板的显示屏,就是下面这个:

i5w6eM

才发现显示屏HDMI驱动板是真的贵,起步价40+,而自己去做的话也差不多这个价格,真搞不明白为啥一个转换板这么复杂。
当然树莓派采用HDMI输出有很多优点,因为HDMI输出是经过GPU的,直接写HDMI的framebuffer就可以了。游戏通过直接HDMI输出的话,帧率可以到50~60帧左右,缺点就是我前面写的需要一个额外的转换板。


树莓派怎么实现程序写HDMI屏幕

大体包括如下步骤:

  • 关闭桌面输出到HDMI。这个可以关闭HDMI显示桌面,命令为systemctl set-default multi-user.target
  • 关闭启动输出和控制台输出。
    在/boot/config.txt文件中添加
hdmi_ignore_hotplug=1
dtoverlay=disable-bt

可以在/boot/cmdline中关闭指针,追加vt.global_cursor_default=0。参见关闭光标,在文章末尾

写HDMI缓冲区的代码是ChatGPT生成的,实测是可用的,如下:

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

int main() {
    int fbfd = open("/dev/fb0", O_RDWR);
    if (fbfd == -1) {
        perror("Error opening framebuffer device");
        exit(1);
    }

    struct fb_var_screeninfo vinfo;
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
        perror("Error reading variable information");
        exit(1);
    }

    int screensize = vinfo.yres_virtual * vinfo.xres_virtual * vinfo.bits_per_pixel / 8;
    char *fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
    if ((int)fbp == -1) {
        perror("Error mapping framebuffer to memory");
        exit(1);
    }

    memset(fbp, 0, screensize); // Clear framebuffer

    int x, y;
    for (y = 100; y < 200; y++) {
        for (x = 100; x < 200; x++) {
            int location = x * (vinfo.bits_per_pixel / 8) + y * vinfo.xres_virtual * (vinfo.bits_per_pixel / 8);
            *(fbp + location) = 255;        // Red
            *(fbp + location + 1) = 0;      // Green
            *(fbp + location + 2) = 0;      // Blue
            *(fbp + location + 3) = 0;      // Transparency
        }
    }

    munmap(fbp, screensize);
    close(fbfd);

    return 0;
}
最后编辑于: 2025 年 01 月 28 日