因为目前我使用的都是树莓派zero、2B这些,所以显示屏主要连接有SPI和HDMI。
为什么会有2种,这个主要源于自己的无知。最早买显示屏的时候,啥也不知道,所以就买了一个SPI显示屏。结果发现帧率跟不上。因为SPI的时钟频率最高50MHZ,驱动一个320 * 480的屏幕最高也就10帧左右,实际上无法满足游戏的帧率刷新要求的。
后面又买了一个HDMI驱动板的显示屏,就是下面这个:
才发现显示屏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
- 关闭控制台上闪烁的光标;
这个找了好久没有太好的办法,可以在启动阶段脚本里执行sudo sh -c 'setterm -cursor off > /dev/tty1'
,也可以参见https://stackoverflow.com/questions/27558741/disabling-the-linux-virtual-console-completely
可以在/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;
}