SPI LCD 颜色相关问题
首先,得先确定显示屏使用的是SPI接口,还是DBI接口,不同的接口,输入数据的解析方式是不一样的。
DBI接口的全称是 Display Bus Serial Interface
,在显示屏数据手册中,一般会说这是SPI接口,所以有人会误认为SPI屏可以使用 normal spi
去直接驱动。
阅读lcd_dbi_if
部分的介绍可以知道,在3线模式时,发送命令前有1位A0用于指示当前发送的是数据,还是命令。而命令后面接着的数据就没有这个A0位了,代表SPI需要在9位和8位之间来回切换,而在读数据时,更是需要延时 dummy clock
才能读数据,normal spi
都很难,甚至无法实现。所以 normal spi
只能模拟4 线的DBI的写操作。
对于R128这类支持DBI接口的CPU,可以选择不去了解SPI。如果需要用到SPI去驱动显示屏,必须把显示屏设置成小端。
RGB565和RGB666
SPI显示屏一般支持RGB444,RGB565和RGB666,RGB444使用的比较少,所以只讨论RGB565和RGB666.
RGB565代表一个点的颜色由2字节组成,也就是R(红色)用5位表示,G(绿色)用6位表示,B(蓝色)用5位表示,如下图所示:
RGB666一个点的颜色由3字节组成,每个字节代表一个颜色,其中每个字节的低2位会无视,如下图所示:
SPI 接口
因为SPI接口的通讯效率不高,所以建议使用RGB565的显示,以 jlt35031c
显示屏为例,他的显示驱动芯片是 ST7789v
,设置显示格式的方式是往 3a
寄存器写入0x55(RGB565
)或者 0x66(RGB666)
,在 R128SDK
中,已经把 jlt35031c
的通讯格式写死为 0x55
,lcd_pixel_fmt
配置选项无效:
sunxi_lcd_cmd_write(sel, 0x3a);
sunxi_lcd_para_write(sel, 0x55);
在例程中,输入的数据是 0xff,0x00,0xff,0x00
,对于SPI接口,是按字节发送。实际上,例程只需要每次发送2字节即可,因为前后发送的都是相同的ff 00,所以没有看出问题。
根据对 565
的数据解析,我们拆分 ff 00
就可以得到红色分量是 0b11111
,也就是 31
,绿色是0b111000
,也就是 56
,,蓝色是 0
.我们等效转换成 RGB888
,有:
R = 31/31*255 = 255
G = 56/63*255 = 226
在调色板输入对应颜色,就可以得到黄色
因为 DBI
通讯效率较高,所以可以使用 RGB565
或者 RGB666
,使用 DBI
接口,也就是 lcd_if
设置为1
时,驱动会根据 lcd_pixel_fmt
配置寄存器,以 SDK
中的 kld2844b.c
为例,这显示屏的显示驱动也是 ST7789
,但是不同的屏幕,厂家封装时已经限制了通讯方式,所以即使是能使用 DBI 接口的驱动芯片的屏幕,或许也用不了DBI。
sunxi_lcd_cmd_write(sel, 0x3A);
if (info[sel].lcd_pixel_fmt == LCDFB_FORMAT_RGB_565 ||
info[sel].lcd_pixel_fmt == LCDFB_FORMAT_BGR_565) {
sunxi_lcd_para_write(sel, 0x55);
if (info[sel].lcd_pixel_fmt == LCDFB_FORMAT_RGB_565)
rotate &= 0xf7;
else
rotate |= 0x08;
} else if (info[sel].lcd_pixel_fmt < LCDFB_FORMAT_RGB_888) {
sunxi_lcd_para_write(sel, 0x66);
if (info[sel].lcd_pixel_fmt == LCDFB_FORMAT_BGRA_8888 ||
info[sel].lcd_pixel_fmt == LCDFB_FORMAT_BGRX_8888 ||
info[sel].lcd_pixel_fmt == LCDFB_FORMAT_ABGR_8888 ||
info[sel].lcd_pixel_fmt == LCDFB_FORMAT_XBGR_8888) {
rotate |= 0x08;
}
} else {
sunxi_lcd_para_write(sel, 0x66);
}
对于 DBI 格式,不再是以字节的形式去解析,而是以字的方式去解析,为了统一,软件已经规定了,RGB565
格式时,字大小是2字节,也就是16位,而 RGB666
格式时,字大小是4字节,也就是32位。
对于 RGB565
格式,同样是设置为 0xff,0x00
。因为屏幕是大端,而芯片存储方式是小端,所以芯片的 DBI 模块,会自动把数据从新排列,也就是实际上 DBI 发送数据时,会先发送0x00
,再发送0xff
,也就是红色分量为0,绿色分量为 0b000111
,也就是7,蓝色分量是 0x11111
,也就是31,我们同样转换成RGB888
G = 7/63*255 = 28
B= 31/31*255 = 255
在调色板上输入,可以得到蓝色。
如果是 RGB666
,虽然占用的是3个字节,但是没有CPU是3字节对齐的,所以需要一次性输入4字节,然后 DBI 硬件模块,会自动舍弃1个字节,软件同意舍弃了最后一个字节。
依旧以例程为例,例程输入了 0xff,0x00,0xff,0x00
,为了方便说明,标准为 0xff(1),0x00(1),0xff(2),0x00(2)
,其中 0x00(2)
会被舍弃掉,然后发送顺序是0xff(2),0x00(1),0xff(1)
,也就是 0xff(2)
是红色分量,0xff(1)
是蓝色分量,混合可以得到紫色。