注:口吻为杂志供稿

DBI液晶介绍

大家应该在淘宝上见过树莓派使用的小型(2.4-3.5英寸之间)TFT板吧,需要插接到GPIO接口上使用。如果各位有玩过单片机的话,应该就会认识这类液晶,他们通常为总线接口(DBI,Display Bus Interface),具体有i80并行总线、68k并行总线、i2c串行总线和spi串行总线这几种。其中68k并行总线和i2c串行总线多见于黑白屏幕。这类总线接口的屏幕通常就是搭配单片机使用的,因为它们接口简单,可以用GPIO模拟实现也可以由简易的控制器实现。同时这类屏幕内置了图形RAM,可以自行对屏幕进行扫描操作,也可以降低单片机处理器的负载。一般多见于4英寸以下的低分辨率(小于480×320)液晶。

前面提到了扫描操作可能有些人不太理解了,我这里也就具体说明下,不想了解或者看不懂的可以跳过这一段。首先液晶屏幕像素点的连接也是像LED点阵一样按照行列来进行矩阵排列的,每行每列都只有一条引线。那么在接通一个行的时候,通过控制这一行不同列的电平,就可以控制这一行的内容显示。利用液晶的拖影和人眼的视觉暂留,不断地扫描不同的行,就可以显示出完整的图像。这就导致屏幕必须要一直刷新才可以显示图像。但是液晶的性质导致其刷新比LED复杂很多。首先,液晶像素并不是像LED一样单向导通的,而是上下极板间存在电势差就会导致液晶分子发生偏转,看起来就是像素被点亮或者熄灭了,所以像LED那样的简单扫描方案会导致很多不应该打开的像素被打开。另外一点是液晶必须要周期性的改变驱动极性,否则会被极化,导致显示内容会逐渐的消失。不过其实这些细节我们不必太关心,我们只要知道,液晶是需要扫描的就可以了。

基于以上介绍,DBI接口液晶就是适合单片机等小型设备使用的一种液晶,一般MP3、MP4和早期的功能手机使用的都是这种液晶。树莓派既然拥有GPIO,自然可以使用这类液晶。由于树莓派是具有硬件SPI控制器的,频率可达恐怖的250Mbps,另一方面所以一般使用SPI接口的DBI的液晶。

由于接口简单,这类的液晶通常可以自制转接板,价格十分便宜。笔者使用的就是淘宝上某款20元左右的2.4英寸TFT+漆包线自制的液晶板。但是这类液晶无法即插即用,现在讲讲使用方法。

使用方式

在树莓派刚刚发布的时候我就尝试过在树莓派上连接DBI液晶,当时那可是相当麻烦。首先得自己写一个使用spi口传输的液晶驱动,在linux里面注册成framebuffer设备,需要自己重新编译树莓派的Linux内核集成进去,当时也没有fbcp这类的屏幕复制软件,为了让主显示映射到液晶上,需要在内核编译选项中把HDMI的驱动给禁用掉或者修改fb顺序,并且这样做是没有硬件加速的,在液晶上播放视频全靠软件解码,而且也不能玩3D游戏。不过现在方便了,有人写了个叫fbtft的驱动就是干这个的,而且兼容性极强,默认可以兼容数十种液晶控制芯片,并且这个驱动被集成到了树莓派官方的系统中,只要简单配置下即可启用。以下我讲一讲使用方法。

首先是硬件连接,如果是买现成的树莓派屏幕模块相比卖家也会提供使用教程吧,我这里讲一讲对于一般的单片机屏幕模块的连接(我自己使用的是单屏幕没有底板,但是也类似)。首先确认接口为SPI,然后弄清楚屏幕的驱动IC(可以在模块的手册或者屏幕的手册中找到),比如我的是ILI9325。接下来查看fbtft默认支持的屏幕列表:https://github.com/notro/fbtft/blob/master/fbtft_device.c,在里面搜索9325,应该能看到如下的片段:

.name = "itdb28_spi",
.spi = &(struct spi_board_info) {
.modalias = "fb_ili9325",
.max_speed_hz = 32000000,
.mode = SPI_MODE_0,
.platform_data = &(struct fbtft_platform_data) {
	.display = {
		.buswidth = 8,
		.backlight = 1,
	},
	.bgr = true,
	.gpios = (const struct fbtft_gpio []) {
		{ "reset", 25 },
		{ "dc", 24 },
		{},
	},
}

其实在那个文件中能找到三种9325设备,第一个使用LoSSI(SPI Mode 3)接口,第二个使用8bit并口,第三个使用普通的SPI接口。我们的屏幕是普通的SPI,应该用第三种。Fbtft确实可以支持8bit并口的屏幕,但是那样不但会花费大量IO口,而且由于是软件模拟时序,不仅速度慢,还会占用大量的CPU时间,所以这里不推荐在树莓派上使用这种接口的屏幕。

在上面的代码中有几个值得注意的地方,比如dc和reset分别接到了24号IO和25号IO上,硬件就按照这个连接。SPI的话,SCK、MOSI、MISO都对连,CS连到树莓派的CS0上。

连接好硬件就可以直接在树莓派上尝试驱动屏幕了。注意首先在raspi-config里面启用SPI后重启,在树莓派上输入sudo modprobe fbtft_device name=itdb28_spi,回车,如果看见屏幕点亮并且显示全黑则为成功,如果为白色即为失败,请检查连线。另外可以试试把终端映射到屏幕上看看是否正常,输入con2fbmap 1 1即可测试。如果方向不对,可以在第一条加载命令后加上orientation=xx来指定方向,另外可以用如speed=48000000来指定速度(48MHz)和fps=25来指定帧率。注意帧率不可过高,会出错。

如果要让每次开机时都自动加载驱动,可以在/etc/modules-load.d/fbtft.conf内加入spi-bcm2835和fbtft_device(因为spi-bcm2835自动加载太慢了,所以在这里手动加载),然后在/etc/modprobe.d/fbtft.conf里面加入选项,比如options fbtft_device name=itdb28_spi之类的即可。

下一步是映射HDMI的显示内容(不是必须的)。在终端内输入以下内容:

sudo apt-get install cmake
git clone https://github.com/tasanakorn/rpi-fbcp
cd rpi-fbcp/
mkdir build
cd build/
cmake ..
make
sudo install fbcp /usr/local/bin/fbcp

这样就完成了fbcp的安装(只需要安装一次),以后要使用时,只要输入fbcp &加上回车就可以了。总体来说,配置难度应该算是大。

效果

其它问题

来讲讲这个方案的其它问题。首先是fbcp的屏幕复制会花费10ms左右时间,也就是存在10ms的延迟。另外fbcp和fbtft驱动是异步工作的,这会进一步加大延迟以及造成画面撕裂。而且画面可以明显观察到存在一个斜向的刷新过程,以下是我拍摄到的情况:

可以观察到相当明显的刷新线。接下来我从原理上说明下这是如何产生的。首先基于前面提到的液晶刷新原理,液晶需要按照一个固定的频率进行刷新(一般为60Hz)才能够显示图像。对于DBI液晶而言,位于液晶面板下方的液晶控制器会自行完成这一操作,数据来源为控制器自带的图像RAM。在这张照片当中,刷新方向为从上到下。另一方面,树莓派不停地把新的显示数据通过SPI发送到液晶控制器,也就是直接写入液晶控制器的RAM。在这里,树莓派内容写入液晶的方向是从右到左,示意图如下:

接下来同时考虑液晶的自刷新,方向为从上到下,为了方便理解,我们假设液晶的刷新是一瞬间刷新1/3屏(实际上并不是),那么效果就成了这个样子:

那么如果液晶的一次是刷新一行,那么效果就是这样了:

但是这个模拟还不完善,这里显示黑的地方实际上不应该是黑的,而是液晶上一帧的内容,而且液晶速度没有那么快,不能在一帧内完全变成新的内容,所以显示内容是好几帧内容的叠加,这就可以解释照片中为何会出现那么多斜线了。另外可以注意到实际上斜线并不是完美的斜线,而是过一会会变竖一段时间,按照上面的原理也就是这段时间内树莓派没有发送数据。这是由于树莓派的SPI数据是通过DMA发送的,每次发送尺寸为4KB,4KB发送完后需要重新填充DMA缓冲才能继续发送。不相信可以做个推算,图上的两条竖线间的像素就是一次发送的像素数,这里为大约7列,每一列有320个像素,16bit色彩下每个像素两个字节,7*320*2=4480,大约就是4KB。那么这个效应就解释清楚了。要讲这个效应就是为了说明,所有DBI屏幕都会因为基本原理的问题而造成动态画面显示效果比较糟糕。使用这个方案一定要注意。 另外强烈不建议用作掌上游戏机的显示屏,因为实测延迟比较高。