SPI LCD 顯示驅(qū)動
簡介
R128 平臺提供了 SPI DBI 的 SPI TFT 接口,具有如下特點(diǎn):
- Supports DBI Type C 3 Line/4 Line Interface Mode
- Supports 2 Data Lane Interface Mode
- Supports data source from CPU or DMA
- Supports RGB111/444/565/666/888 video format
- Maximum resolution of RGB666 240 x 320@30Hz with single data lane
- Maximum resolution of RGB888 240 x 320@60Hz or 320 x 480@30Hz with dual data lane
- Supports tearing effect
- Supports software flexible control video frame rate
同時(shí),提供了SPILCD驅(qū)動框架以供 SPI 屏幕使用。
模塊驅(qū)動
MENUCONFIG配置說明
SPILCD 模塊 menuconfig 的配置如下(以選擇kld2844b屏為例):
Drivers Options --- >
soc related device drivers --- >
SPILCD Devices --- >
[*] DISP Driver Support(spi_lcd)
[*] spilcd hal APIs test //spilcd模塊測試用例
LCD_FB panels select --- > //spilcd屏驅(qū)動配置
[*] LCD support kld2844B panel
[ ] LCD support kld35512 panel
Board select --- >
[ ] board kld2844b support //板級顯示使用顯示驅(qū)動私有方式的配置項(xiàng),而使用sys_config.fex方式不用配置
另外可能需依賴的配置項(xiàng)有:
- DRIVERS_SPI
- DRIVERS_DBI
- DRIVERS_PWM
源碼結(jié)構(gòu)介紹
源碼結(jié)構(gòu)及主要驅(qū)動文件如下:
spilcd/
├── lcd_fb/
│ ├── dev_lcd_fb.c # spilcd driver 層
│ ├── disp_display.c
│ ├── disp_lcd.c
│ ├── lcd_fb_intf.c
│ └── panels/ # lcd驅(qū)動相關(guān)
│ ├── kld2844b.c
│ ├── lcd_source.c
│ ├── panels.c
│ └── panels.h
└── soc/
├── disp_board_config.c # 板級配置解析
└── kld2844b_config.c # 顯示私有方式的板級配置文件
模塊參數(shù)配置
當(dāng)前板級顯示支持兩種配置方法,一是使用 sys_config.fex
的方式進(jìn)行配置,二是在不支持sys_config.fex
情況下,可以通過顯示驅(qū)動私有的方式進(jìn)行配置。下面分別對兩種方式進(jìn)行說明。
使用 SYS_CONFIG.FEX 的方式進(jìn)行配置
FreeRTOS系統(tǒng)路徑:board/芯片名/方案名/configs/sys_config.fex
配置文件具體要看芯片方案所實(shí)際使用的,也可能使用的配置文件名稱為sys_config_xxx.fex(xx是存儲方案的標(biāo)識,例如sys_config_nor.cfg、sys_config_nand.cfg)
具體配置舉例如下:
;----------------------------------------------------------------------------------
;lcd_fb0 configuration
;----------------------------------------------------------------------------------
[lcd_fb0]
lcd_used = 1 ; 使用顯示屏
lcd_model_name = "spilcd" ; 模型:spilcd
lcd_driver_name = "jlt35031c" ; 屏幕驅(qū)動:jlt35031c
lcd_x = 320 ; 屏幕寬分辨率
lcd_y = 480 ; 屏幕高分辨率
lcd_width = 49 ; 屏幕物理寬度
lcd_height = 74 ; 屏幕物理高度
lcd_data_speed = 60 ; SPI 驅(qū)動頻率 60MHz
lcd_pwm_used = 1 ; lcd使用pwm背光
lcd_pwm_ch = 1 ; lcd使用pwm背光通道1
lcd_pwm_freq = 5000 ; lcd使用pwm背光頻率5000Hz
lcd_pwm_pol = 0 ; lcd使用pwm背光相位0
lcd_if = 0 ; lcd使用spi接口,0-spi, 1-dbi
lcd_pixel_fmt = 11
lcd_dbi_fmt = 2
lcd_dbi_clk_mode = 1
lcd_dbi_te = 1
fb_buffer_num = 2
lcd_dbi_if = 4
lcd_rgb_order = 0
lcd_fps = 60
lcd_spi_bus_num = 1
lcd_frm = 2
lcd_gamma_en = 1
lcd_backlight = 100
lcd_power_num = 0
lcd_gpio_regu_num = 0
lcd_bl_percent_num = 0
lcd_spi_dc_pin = port:PA19< 1 >< 0 >< 3 >< 0 > ; DC腳
;RESET Pin
lcd_gpio_0 = port:PA20< 1 >< 0 >< 2 >< 0 > ; 復(fù)位腳
lcd_driver_name
Lcd屏驅(qū)動的名字(字符串),必須與屏驅(qū)動中strcut __lcd_panel
變量的name
成員一致。
lcd_model_name
Lcd屏模型名字,非必須,可以用于同個(gè)屏驅(qū)動中進(jìn)一步區(qū)分不同屏。
lcd_if
Lcd Interface
設(shè)置相應(yīng)值的對應(yīng)含義為:
0:spi接口
1:dbi接口
spi
接口就是俗稱的4線模式,這是因?yàn)榘l(fā)送數(shù)據(jù)時(shí)需要額外借助DC
線來區(qū)分命令和數(shù)據(jù),與sclk
,cs
和sda
共四線。
如果設(shè)置了dbi
接口,那么還需要進(jìn)一步區(qū)分dbi
接口,需要設(shè)置 lcd_dbi_if
lcd_dbi_if
Lcd dbi 接口設(shè)置。
這個(gè)參數(shù)只有在lcd_if=1
時(shí)才有效。
設(shè)置相應(yīng)值的對應(yīng)含義為:
0:L3I1
1:L3I2
2:L4I1
3:L4I2
4:D2LI
所有模式在發(fā)送數(shù)據(jù)時(shí)每個(gè)周期的比特?cái)?shù)量根據(jù)不同像素格式不同而不同。
L3I1
和L3I2
是三線模式(不需要DC
腳),區(qū)別是讀時(shí)序,也就是是否需要額外腳來讀寄存器。讀寫時(shí)序圖如下:
L4I1
和L4I2
是四線模式,與spi接口協(xié)議一樣,區(qū)別是DC腳的控制是否自動化控制,另外I2和I1的區(qū)別是讀時(shí)序,也就是否需要額外腳來讀取寄存器。
D2LI
是兩data lane模式。發(fā)送命令部分時(shí)序與讀時(shí)序與L3I1
一致,下圖是發(fā)送數(shù)據(jù)時(shí)的時(shí)序,不同像素格式時(shí)鐘周期數(shù)量不一樣。
lcd_dbi_fmt
DBI
接口像素格式。
0:RGB111
1:RGB444
2:RGB565
3:RGB666
4:RGB888
選擇的依據(jù)是接收端屏Driver IC
的支持情況,請查看Driver IC
手冊或詢問屏廠。
然后必須配合lcd_pixel_fmt
的選擇,比如說選RGB565時(shí),lcd_pixel_fmt
也要選565格式。
lcd_dbi_te
使能te觸發(fā)。
te即(Tearing effect),也就是撕裂的意思,由于讀寫不同導(dǎo)致撕裂現(xiàn)象,te腳的功能就是用于同步讀寫,te腳的頻率也就是屏的刷新率,所以te腳也可以看做vsync腳(垂直同步腳)
0: 禁止te
1: 下降沿觸發(fā)
2: 上升沿觸發(fā)
查看帶te腳的屏進(jìn)一步說明。
lcd_dbi_clk_mode
選擇dbi時(shí)鐘的行為模式。
0:自動停止。有數(shù)據(jù)就有時(shí)鐘,沒發(fā)數(shù)據(jù)就沒有
1:一直保持。無論發(fā)不發(fā)數(shù)據(jù)都有時(shí)鐘
注意上面的選項(xiàng)關(guān)系屏兼容性。
lcd_rgb_order
輸入圖像數(shù)據(jù)rgb順序識別設(shè)置,僅當(dāng)lcd_if=1時(shí)有效。
0:RGB
1:RBG
2:GRB
3:GBR
4:BRG
5:BGR
6:G_1RBG_0
7:G_0RBG_1
8:G_1BRG_0
9:G_0BRG_1
非RGB565格式用0到5即可。
針對rgb565格式說明如下:
rgb565格式會遇到大小端問題,arm平臺和PC平臺存儲都是小端(little endian,低字節(jié)放在低地址,高字節(jié)放在高地址),但是許多spi屏都是默認(rèn)大端(Big Endian)。
也就是存儲的字節(jié)順序和發(fā)送的字節(jié)順序不對應(yīng)。
這個(gè)時(shí)候選擇6以下,DBI接口就會自動將小端轉(zhuǎn)成大端。
如果遇到默認(rèn)是小端的spi屏,則需要選擇6以上,DBI接口會自動用回小端方式。
6以上格式這樣解釋:
R是5比特,G是6比特,B是5比特,再把G拆成高3位(G_1)和低3位(G_0)
所以以下兩種順序:
- R-G_1-G_0-B,大端。
- G_0-B-R-G_1,對應(yīng)上面的9,小端。
lcd_x
顯示屏的水平像素?cái)?shù)量,注意如果屏支持橫豎旋轉(zhuǎn),那么lcd_x和lcd_y也要對調(diào)。
lcd_y
顯示屏的行數(shù),注意如果屏支持橫豎旋轉(zhuǎn),那么lcd_x和lcd_y也要對調(diào)。
lcd_data_speed
用于設(shè)置spi/dbi接口時(shí)鐘的速率,單位MHz。
- 發(fā)送端(SOC)的最大限制是100MHz。
- 接收端(屏Driver IC)的限制,請查看對應(yīng)Driver IC手冊或者詢問屏廠支持。
- 超出以上限制都有可能導(dǎo)致顯示異常。
lcd_fps
設(shè)置屏的刷新率,單位Hz。當(dāng)lcd_dbi_te使能時(shí),這個(gè)值設(shè)置無效。
lcd_pwm_used
是否使用pwm。
此參數(shù)標(biāo)識是否使用pwm用以背光亮度的控制。
lcd_pwm_ch
Pwm channel used
此參數(shù)標(biāo)識使用的Pwm通道。
lcd_pwm_freq
Lcd backlight PWM Frequency
這個(gè)參數(shù)配置PWM信號的頻率,單位為Hz。
lcd_pwm_pol
Lcd backlight PWM Polarity
這個(gè)參數(shù)配置PWM信號的占空比的極性。設(shè)置相應(yīng)值對應(yīng)含義為:
0:active high
1:active low
lcd_pwm_max_limit
Lcd backlight PWM
最高限制,以亮度值表示。
比如150,則表示背光最高只能調(diào)到150,0~255范圍內(nèi)的亮度值將會被線性映射到0~150范圍內(nèi)。用于控制最高背光亮度,節(jié)省功耗。
lcd_backlight
默認(rèn)背光值,取值范圍0到255,值越大越亮。
lcd_bl_en
背光使能腳定義
lcd_spi_dc_pin
指定作為DC的管腳,用于spi接口時(shí)。
lcd_gpio_x
x表示數(shù)字。如果有多個(gè)gpio腳需要控制,則定義lcd_gpio_0,lcd_gpio_1等。
lcd_spi_bus_num
選擇spi總線id,只有spi1支持DBI協(xié)議,所以這里一般選擇1。
取值范圍:0到1。
lcd_pixel_fmt
選擇傳輸數(shù)據(jù)的像素格式。
可選值如下,當(dāng)你更換RGB分量順序的時(shí)候,也得相應(yīng)修改lcd_rgb_order,或者修改屏驅(qū)動的rgb分量順序(一般是3Ah寄存器)。
DBI接口只支持RGB32和RGB16的情況。
SPI接口只支持RGB16的情況。
enum lcdfb_pixel_format {
LCDFB_FORMAT_ARGB_8888 = 0x00, // MSB A-R-G-B LSB
LCDFB_FORMAT_ABGR_8888 = 0x01,
LCDFB_FORMAT_RGBA_8888 = 0x02,
LCDFB_FORMAT_BGRA_8888 = 0x03,
LCDFB_FORMAT_XRGB_8888 = 0x04,
LCDFB_FORMAT_XBGR_8888 = 0x05,
LCDFB_FORMAT_RGBX_8888 = 0x06,
LCDFB_FORMAT_BGRX_8888 = 0x07,
LCDFB_FORMAT_RGB_888 = 0x08,
LCDFB_FORMAT_BGR_888 = 0x09,
LCDFB_FORMAT_RGB_565 = 0x0a,
LCDFB_FORMAT_BGR_565 = 0x0b,
LCDFB_FORMAT_ARGB_4444 = 0x0c,
LCDFB_FORMAT_ABGR_4444 = 0x0d,
LCDFB_FORMAT_RGBA_4444 = 0x0e,
LCDFB_FORMAT_BGRA_4444 = 0x0f,
LCDFB_FORMAT_ARGB_1555 = 0x10,
LCDFB_FORMAT_ABGR_1555 = 0x11,
LCDFB_FORMAT_RGBA_5551 = 0x12,
LCDFB_FORMAT_BGRA_5551 = 0x13,
};
fb_buffer_num
顯示framebuffer數(shù)量,為了平滑顯示,這里一般是2個(gè),為了省內(nèi)存也可以改成1。
模塊 SYS_CONFIG.FEX 配置案例
典型2 data lane配置
一些屏支持雙數(shù)據(jù)線傳輸以加快數(shù)據(jù)傳輸速度,此時(shí)需要走DBI協(xié)議,典型配置如下:
[lcd_fb0]
lcd_used = 1
lcd_driver_name = "kld2844b"
lcd_if = 1
lcd_dbi_if = 4
lcd_data_speed = 60
lcd_spi_bus_num = 1
lcd_x = 320
lcd_y = 240
lcd_pwm_used = 1
lcd_pwm_ch = 4
lcd_pwm_freq = 5000
lcd_pwm_pol = 0
lcd_pixel_fmt = 0
lcd_dbi_fmt = 3
lcd_rgb_order = 0
fb_buffer_num = 2
lcd_backlight = 200
lcd_fps = 60
lcd_dbi_te = 0
lcd_bl_en = port:PB04< 1 >< 0 >< default >< 1 >
lcd_gpio_0 = port:PB02< 1 >< 0 >< default >< 1 >
- 硬件連接上,第二根數(shù)據(jù)腳連接到原來1 data lane的DC腳,可以這樣理解:2數(shù)據(jù)線在傳輸數(shù)據(jù)時(shí)就自帶D/C(Data/Commend)信息了,所以原來的DC腳就可以空出來作為第二根數(shù)據(jù)線了。
- 屏驅(qū)動上,需要使能2 data lane模式,具體寄存器查看對應(yīng)driverIC手冊或者詢問屏廠。
- 這里的針對對2 data lane的關(guān)鍵參數(shù)是lcd_if,lcd_dbi_if,lcd_dbi_fmt和lcd_spi_bus_num。
- lcd_x和lcd_y是屏分辨率。如果屏支持旋轉(zhuǎn)(橫豎旋轉(zhuǎn)),這里也需要對調(diào)。
- lcd_pwm開頭,lcd_backlight和lcd_bl_en)的是背光相關(guān)設(shè)置,如果有相關(guān)硬件連接的話。
- lcd_pixel_fmt和fb_buffer_num是顯示framebuffer的設(shè)置。
- lcd_gpio_開頭的是自定義gpio的設(shè)置(比如復(fù)位腳)。
- lcd_fps和lcd_dbi_te是刷新方式相關(guān)的設(shè)置。
原SPI接口屏配置
如果IC支持DBI接口,那么就沒有必要用SPI接口,DBI接口其協(xié)議能覆蓋所有情況。
一些IC不支持DBI,那么只能用spi接口(通過設(shè)置lcd_if),如果使用spi接口,它有一些限制。
- 不支持2 data lane。
- 必須指定DC腳。這是由于spi協(xié)議不會自動控制DC腳來區(qū)分?jǐn)?shù)據(jù)命令,通過設(shè)置lcd_spi_dc_pin可以完成這個(gè)目的,這跟管腳不必用spi里面的腳。
- 只支持rgb565的像素格式。由于只有單data lane,速度過慢,rgb565以上格式都不現(xiàn)實(shí)。
[lcd_fb0]
lcd_used = 1
lcd_driver_name = "kld2844b"
lcd_if = 0
lcd_data_speed = 60
lcd_spi_bus_num = 1
lcd_x = 320
lcd_y = 240
lcd_pwm_used = 1
lcd_pwm_ch = 4
lcd_pwm_freq = 5000
lcd_pwm_pol = 0
lcd_pixel_fmt = 10
lcd_rgb_order = 0
fb_buffer_num = 2
lcd_backlight = 200
lcd_fps = 60
lcd_dbi_te = 0
lcd_bl_en = port:PB04<1><0><1>
lcd_gpio_0 = port:PB02<1><0><1>
lcd_spi_dc_pin = port:PA19<1><0><3><1>
帶te腳的屏
te即(Tearing effect),也就是撕裂的意思,是由于讀寫不同步導(dǎo)致撕裂現(xiàn)象,te腳的功能就是用于同步讀寫,te腳的頻率也就是屏的刷新率,所以te腳也可以看做vsync腳(垂直同步腳)。
- 硬件設(shè)計(jì)階段,需要將屏的te腳連接到IC的DBI接口的te腳。
- 配置上接口使用dbi接口。
- 然后使能lcd_dbi_te。
- 屏驅(qū)動使能te功能,寄存器一般是35h,詳情看屏對應(yīng)的driver IC手冊。
- 屏驅(qū)動設(shè)置幀率,根據(jù)屏能接受的傳輸速度選擇合理的幀率(比如ST7789H2里面是通過c6h來設(shè)置te頻率)。
[lcd_fb0]
lcd_used = 1
lcd_driver_name = "kld2844b"
lcd_if = 1
lcd_dbi_if = 4
lcd_data_speed = 60
lcd_spi_bus_num = 1
lcd_x = 320
lcd_y = 240
lcd_pwm_used = 1
lcd_pwm_ch = 4
lcd_pwm_freq = 5000
lcd_pwm_pol = 0
lcd_pixel_fmt = 0
lcd_dbi_fmt = 3
lcd_rgb_order = 0
fb_buffer_num = 2
lcd_backlight = 200
lcd_fps = 60
lcd_dbi_te = 1
lcd_bl_en = port:PB04<1><0><1>
lcd_gpio_0 = port:PB02<1><0><1>
橫豎屏旋轉(zhuǎn)
- 平臺沒有硬件旋轉(zhuǎn)功能,軟件旋轉(zhuǎn)太慢而且耗費(fèi)CPU。
- 不少spi屏支持內(nèi)部旋轉(zhuǎn),需要在屏驅(qū)動初始化的時(shí)候進(jìn)行設(shè)置,一般是36h寄存器。
// 轉(zhuǎn)成橫屏
sunxi_lcd_cmd_write(sel, 0x36);
sunxi_lcd_para_write(sel, 0xa0); - lcd_fb0的配置中需要將lcd_x和lcd_y的值對調(diào),此時(shí)軟件將屏視為橫屏。
- 屏內(nèi)部旋轉(zhuǎn)出圖效果可能會變差,建議選屏的時(shí)候直接選好方向。
幀率控制
屏的刷新率受限于多方面:
- SPI/DBI硬件傳輸速度,也就是時(shí)鐘腳的頻率。設(shè)置lcd_data_speed可以設(shè)置硬件傳輸速度,最大不超過100MHz。如果屏能正常接收,這個(gè)值自然是越大越好。
- 屏driver IC接收能力。Driver IC手冊中會提到屏的能接受的最大sclk周期。
- 使用2 data lane還是1 data lane,理論上2 data lane的速度會翻倍。見典型2 data lane配置。
- 像素格式。像素格式?jīng)Q定需要傳輸?shù)臄?shù)據(jù)量,顏色數(shù)量越小的像素格式,幀率越高,但是效果越差。
- 帶te腳的屏一節(jié)中我們知道,te相關(guān)設(shè)置直接影響到屏刷新率。
- 如果不支持te,可以通過設(shè)置lcd_fps來控制幀率,你需要根據(jù)第一點(diǎn)和第二點(diǎn)選擇一個(gè)合適的值。
背光控制
- 硬件需要支持pwm背光電路。
- 驅(qū)動支持pwm背光調(diào)節(jié),只需要配置好lcd_pwm開頭,lcd_backlight和lcd_bl_en等背光相關(guān)配置即可。
像素格式相關(guān)
- lcd_pixel_fmt,這個(gè)設(shè)置項(xiàng)用于設(shè)置fbdev的像素格式。
- lcd_dbi_fmt,這個(gè)用于設(shè)置DBI接口發(fā)送的像素格式。
SPI/DBI發(fā)送數(shù)據(jù)的時(shí)候沒有必要發(fā)送alpha通道,但是應(yīng)用層卻有對應(yīng)的alpha通道,比如ARGB8888格式。
這個(gè)時(shí)候硬件會自動幫我們處理好alpha通道,所以lcd_pixel_fmt選擇有alpha通道的格式時(shí),lcd_dbi_fmt可以選rgb666或者rgb888,不用和它一樣。
電源配置
有多個(gè)電源的話,就用lcd_power1,lcd_power2......然后屏驅(qū)動里面調(diào)用sunxi_lcd_power_enable接口即可。
GPIO配置說明
lcd_bl_en、lcd_spi_dc_pin以及lcd_gpio_x都是屬于GPIO屬性類型。
下面以lcd_spi_dc_pin為例,具體說明GPIO屬性的參數(shù)值含義:
lcd_spi_dc_pin = port:PA19< 1 >< 0 >< 3 >< 1 >
引腳說明:port: 端口 < 復(fù)用功能 >< 上下拉 >< 驅(qū)動能力 >< 輸出值 >
等式右邊的從左到右5個(gè)字段代表的具體含義如下:
PA19
:端口,表示GPIO管腳。PA
表示PA組管腳,19
表示第19根管腳;即PA19管腳。<1>
:復(fù)用功能,表示GPIO復(fù)用類型。1表示將PA19選擇為通用GPIO功能,0為輸入,1為輸出。<0>
:上下拉,表示內(nèi)置電阻。使用0的話,表示內(nèi)部電阻高阻態(tài),如果是1則是內(nèi)部電阻上拉,2就代表內(nèi)部電阻下拉。其它數(shù)據(jù)無效。<3>
:表示驅(qū)動能力。1是默認(rèn)等級,數(shù)字越大驅(qū)動能力越強(qiáng),最大是3。<1>
:表示默認(rèn)輸出電平值。0為低電平,1為高電平。
多個(gè)顯示
- 確定硬件有沒有多余的spi/dbi接口。
- 需要在sys_config.fex里面新增lcd_fb1,配置方式與lcd_fb0一樣(或者在顯示私有方式的板級配置文件里面新增
g_lcd1_config
,配置方式與g_lcd0_config一樣),其中lcd_spi_bus_num不能一樣。
依賴驅(qū)動配置
spilcd模塊依賴spi,dbi,pwm等驅(qū)動。
使用顯示私有方式進(jìn)行配置
路徑:rtos-hal/hal/source/spilcd/soc/
具體板級顯示配置文件可參考該路徑下的 kld2844b_config.c
配置文件。
該文件模仿 sys_config.fex
來配置一些板級相關(guān)的資源,源文件中需要定義4個(gè)全局變量:g_lcd0_config
、g_lcd1_config
、g_lcd0_config_len
和g_lcd1_config_len
,且變量名字固定,不能修改。
具體說明如下:
g_lcd0_config
:第一個(gè)屏的配置變量,struct property_t
數(shù)據(jù)類型。g_lcd1_config
:第二個(gè)屏的配置變量,struct property_t
數(shù)據(jù)類型。g_lcd0_config_len
和g_lcd1_config_len
是對應(yīng)上面兩個(gè)數(shù)組變量的長度,照搬即可。
struct property_t
數(shù)據(jù)類型,用于定義一個(gè)屬性的信息:
- 屬性名字。對應(yīng)其成員
name
,字符串。 - 屬性類型。對應(yīng)其成員
type
,看enum proerty_type
的定義,共有整型,字符串,GPIO,專用pin和電源。 - 屬性值。根據(jù)上面的屬性類型來選擇
union
中成員來賦值。
對于上述常用的屬性類型舉例如下:
整型:
{
.name = "lcd_used",
.type = PROPERTY_INTGER,
.v.value = 1,
},
字符串:
{
.name = "lcd_driver_name",
.type = PROPERTY_STRING,
.v.str = "kld2844b",
},
GPIO:
{
.name = "lcd_spi_dc_pin",
.type = PROPERTY_GPIO,
.v.gpio_list = {
.gpio = GPIOA(19),
.mul_sel = GPIO_DIRECTION_OUTPUT,
.pull = 0,
.drv_level = 3,
.data = 1,
},
},
g_lcd0_config的含義與前述的使用sys_config.fex方式中的lcd_fb0一致。
編寫屏驅(qū)動
屏驅(qū)動源碼位置:
rtos-hal/hal/source/spilcd/lcd_fb/panels
- 在屏驅(qū)動源碼位置下拷貝現(xiàn)有一個(gè)屏驅(qū)動,包括頭文件和源文件,然后將文件名改成有意義的名字,比如屏型號。
- 修改源文件中的
strcut __lcd_panel
變量的名字,以及這個(gè)變量成員name
的名字,這個(gè)名字必須和sys_config.fex
中[lcd_fb0]
(或板級配置文件中g_lcd0_config
)的lcd_driver_name
一致。 - 在屏驅(qū)動目錄下修改
panel.c
和panel.h
。在全局結(jié)構(gòu)體變量panel_array
中新增剛才添加strcut __lcd_panel
的變量指針。panel.h
中新增strcut __lcd_panel
的聲明。并用宏括起來。 - 修改
rtos-hal/hal/source/spilcd/lcd_fb/panels/Kconfig
,新增一個(gè)config,與第三點(diǎn)提到的宏對應(yīng)。 - 修改
rtos-hal/hal/source/spilcd/lcd_fb/
路徑下的Makefile文件。給lcd_fb-obj變量新增剛才加入的源文件對應(yīng).o
。 - 根據(jù)本手冊以及屏手冊,Driver IC手冊修改sys_config.fex中的[lcd_fb0]節(jié)點(diǎn)(或顯示私有方式的板級配置文件中的
g_lcd0_config
配置變量)下面的屬性 - 實(shí)現(xiàn)屏源文件中的
LCD_open_flow
,LCD_close_flow
,LCD_panel_init
,LCD_power_on
等函數(shù)
開關(guān)屏流程函數(shù)解析
開關(guān)屏的操作流程如下圖所示。
其中,LCD_open_flow和LCD_close_flow稱為開關(guān)屏流程函數(shù),該函數(shù)利用LCD_OPEN_FUNC進(jìn)行注冊回調(diào)函數(shù),先注冊先執(zhí)行,可以注冊多個(gè),不限制數(shù)量。
LCD_open_flow
功能:初始化開屏的步驟流程。
原型:
static __s32 LCD_open_flow(__u32 sel)
函數(shù)常用內(nèi)容為:
static __s32 LCD_open_flow(__u32 sel)
{
LCD_OPEN_FUNC(sel, LCD_power_on,10);
LCD_OPEN_FUNC(sel, LCD_panel_init, 50);
LCD_OPEN_FUNC(sel, lcd_fb_black_screen, 100);
LCD_OPEN_FUNC(sel, LCD_bl_open, 0);
return 0;
}
如上,初始化整個(gè)開屏的流程步驟為四個(gè):
- 打開LCD電源,再延遲10ms。
- 初始化屏,再延遲50ms;(不需要初始化的屏,可省掉此步驟)。
- 向屏發(fā)送全黑的數(shù)據(jù)。這一步驟是必須的,而且需要在開背光之前。
- 打開背光,再延遲0ms。
LCD_open_flow
函數(shù)只會系統(tǒng)初始化的時(shí)候調(diào)用一次,執(zhí)行每個(gè) LCD_OPEN_FUNC
即是把對應(yīng)的開屏步驟函數(shù)進(jìn)行注冊, 并沒有立即執(zhí)行該開屏步驟函數(shù) 。LCD_open_flow函數(shù)的內(nèi)容必須統(tǒng)一用 LCD_OPEN_FUNC(sel, function, delay_time)
進(jìn)行函數(shù)注冊的形式,確保正常注冊到開屏步驟中。
LCD_OPEN_FUNC的第二個(gè)參數(shù)是前后兩個(gè)步驟的延時(shí)長度,單位ms,注意這里的數(shù)值請按照屏手冊規(guī)定去填,亂填可能導(dǎo)致屏初始化異?;蛘唛_關(guān)屏?xí)r間過長,影響用戶體驗(yàn)。
LCD_close_flow
功能:初始化關(guān)屏的步驟流程。
原型:
static __s32 LCD_close_flow(__u32 sel)
函數(shù)常用內(nèi)容為:
static __s32 LCD_close_flow(__u32 sel)
{
LCD_CLOSE_FUNC(sel, LCD_bl_close, 50);
LCD_CLOSE_FUNC(sel, LCD_panel_exit, 10);
LCD_CLOSE_FUNC(sel, LCD_power_off, 10);
return 0;
}
- LCD_bl_close,是關(guān)背光,關(guān)完背光在處理其它事情,不會影響用戶視覺。
- LCD_panel_exit,發(fā)送命令讓屏退出工作狀態(tài)。
- 關(guān)電復(fù)位,讓屏徹底關(guān)閉。
LCD_OPEN_FUNC
功能:注冊開屏步驟函數(shù)到開屏流程中,記住這里是注冊不是執(zhí)行!
原型:
void LCD_OPEN_FUNC(__u32 sel, LCD_FUNC func, __u32 delay)
參數(shù)說明:
func是一個(gè)函數(shù)指針,其類型是:void (*LCD_FUNC) (__u32 sel)
,用戶自己定義的函數(shù)必須也要用統(tǒng)一的形式。比如:
void user_defined_func(__u32 sel)
{
//do something
}
delay是執(zhí)行該步驟后,再延遲的時(shí)間,時(shí)間單位是毫秒。
LCD_power_on
這是開屏流程中第一步,一般在這個(gè)函數(shù)使用sunxi_lcd_gpio_set_value進(jìn)行GPIO控制,用sunxi_lcd_power_enable函數(shù)進(jìn)行電源開關(guān)。
參考屏手冊里面的上電時(shí)序(Power on sequence)。
LCD_panel_init
這是開屏流程第二步,一般使用sunxi_lcd_cmd_write和sunxi_lcd_para_write對屏寄存器進(jìn)行初始化。
請向屏廠索要初始化寄存器代碼或者自行研究屏Driver IC手冊。
lcd_fb_black_screen
向屏傳輸全黑數(shù)據(jù)的接口,是必須的,否則打開背光后,呈現(xiàn)的將是雪花屏。
LCD_bl_open
這是背光使能,固定調(diào)用。
- sunxi_lcd_backlight_enable, 打開lcd_bl_en腳。
- sunxi_lcd_pwm_enable, 使能pwm。
LCD_bl_close
這是關(guān)閉背光。固定調(diào)用下面兩個(gè)函數(shù),分別是:
- sunxi_lcd_backlight_disable,lcd_bl_en關(guān)閉
- sunxi_lcd_pwm_disable, 關(guān)閉pwm。
LCD_power_off
這是關(guān)屏流程中最后一步,一般在這個(gè)函數(shù)使用sunxi_lcd_gpio_set_value進(jìn)行GPIO控制,用sunxi_lcd_power_enable函數(shù)進(jìn)行電源開關(guān)。
參考屏手冊里面的下電時(shí)序(Power off sequence)。
sunxi_lcd_delay_ms
函數(shù):sunxi_lcd_delay_ms/sunxi_lcd_delay_us
功能:延時(shí)函數(shù),分別是毫秒級別/微秒級別的延時(shí)。
原型:s32 sunxi_lcd_delay_ms(u32 ms); / s32 sunxi_lcd_delay_us(u32 us);
sunxi_lcd_backlight_enable
函數(shù):sunxi_lcd_backlight_enable/ sunxi_lcd_backlight_disable
功能:打開/關(guān)閉背光,操作的是lcd_bl_en。
原型:
void sunxi_lcd_backlight_enable(u32 screen_id);
void sunxi_lcd_backlight_disable(u32 screen_id);
sunxi_lcd_pwm_enable
函數(shù):sunxi_lcd_pwm_enable / sunxi_lcd_pwm_disable
功能:打開/關(guān)閉pwm控制器,打開時(shí)pwm將往外輸出pwm波形。對應(yīng)的是lcd_pwm_ch所對應(yīng)的那一路pwm。
原型:
s32 sunxi_lcd_pwm_enable(u32 screen_id);
s32 sunxi_lcd_pwm_disable(u32 screen_id);
sunxi_lcd_power_enable
函數(shù):sunxi_lcd_power_enable / sunxi_lcd_power_disable
功能:打開/關(guān)閉Lcd電源,操作的是板級配置文件中的lcd_power/lcd_power1/lcd_power2
。(pwr_id標(biāo)識電源索引)。
原型:
void sunxi_lcd_power_enable(u32 screen_id, u32 pwr_id);
void sunxi_lcd_power_disable(u32 screen_id, u32 pwr_id);
- pwr_id = 0:對應(yīng)于配置文件中的lcd_power。
- pwr_id = 1:對應(yīng)于配置文件中的lcd_power1。
- pwr_id = 2:對應(yīng)于配置文件中的lcd_power2。
- pwr_id = 3:對應(yīng)于配置文件中的lcd_power3。
sunxi_lcd_cmd_write
函數(shù):sunxi_lcd_cmd_write
功能:使用spi/dbi發(fā)送命令。
原型:s32 sunxi_lcd_cmd_write(u32 screen_id, u8 cmd);
sunxi_lcd_para_write
函數(shù):sunxi_lcd_para_write
功能:使用spi/dbi發(fā)送參數(shù)。
原型:s32 sunxi_lcd_para_write(u32 screen_id, u8 para);
sunxi_lcd_gpio_set_value
函數(shù):sunxi_lcd_gpio_set_value
功能:LCD_GPIO PIN腳上輸出高電平或低電平。
原型:s32 sunxi_lcd_gpio_set_value(u32 screen_id, u32 io_index, u32 value);
參數(shù)說明:
- io_index = 0:對應(yīng)于配置文件中的lcd_gpio_0。
- io_index = 1:對應(yīng)于配置文件中的lcd_gpio_1。
- io_index = 2:對應(yīng)于配置文件中的lcd_gpio_2。
- io_index = 3:對應(yīng)于配置文件中的lcd_gpio_3。
- value = 0:對應(yīng)IO輸出低電平。
- Value = 1:對應(yīng)IO輸出高電平。
只用于該GPIO定義為輸出的情形。
sunxi_lcd_gpio_set_direction
函數(shù):sunxi_lcd_gpio_set_direction
功能:設(shè)置LCD_GPIO PIN腳為輸入或輸出模式。
原型:
s32 sunxi_lcd_gpio_set_direction(u32 screen_id, u32 io_index, u32 direction);
參數(shù)說明:
- io_index = 0:對應(yīng)于配置文件中的lcd_gpio_0。
- io_index = 1:對應(yīng)于配置文件中的lcd_gpio_1。
- io_index = 2:對應(yīng)于配置文件中的lcd_gpio_2。
- io_index = 3:對應(yīng)于配置文件中的lcd_gpio_3。
- direction = 0:對應(yīng)IO設(shè)置為輸入。
- direction = 1:對應(yīng)IO設(shè)置為輸出。
模塊測試用例
#include < stdio.h >
#include < stdlib.h >
#include < stdint.h >
#include < string.h >
#include < unistd.h >
#include < hal_cache.h >
#include < hal_mem.h >
#include < hal_log.h >
#include < hal_cmd.h >
#include < hal_lcd_fb.h >
static uint32_t width;
static uint32_t height;
static long int screensize = 0;
static char *fbsmem_start = 0;
static void lcdfb_fb_init(uint32_t yoffset, struct fb_info *p_info)
{
p_info- >screen_base = fbsmem_start;
p_info- >var.xres = width;
p_info- >var.yres = height;
p_info- >var.xoffset = 0;
p_info- >var.yoffset = yoffset;
}
int show_rgb(unsigned int sel)
{
int i = 0, ret = -1;
struct fb_info fb_info;
int bpp = 4;
unsigned char color[4] = {0xff,0x0,0xff,0x0};
width = bsp_disp_get_screen_width(sel);
height = bsp_disp_get_screen_height(sel);
screensize = width * bpp * height;
fbsmem_start = hal_malloc_coherent(screensize);
hal_log_info("width = %d, height = %d, screensize = %d, fbsmem_start = %xn",
width, height, screensize, fbsmem_start);
memset(fbsmem_start, 0, screensize);
for (i = 0; i < screensize / bpp; ++i) {
memcpy(fbsmem_start+i*bpp, color, bpp);
}
memset(&fb_info, 0, sizeof(struct fb_info));
lcdfb_fb_init(0, &fb_info);
hal_dcache_clean((unsigned long)fbsmem_start, screensize);
bsp_disp_lcd_set_layer(sel, &fb_info);
hal_free_coherent(fbsmem_start);
return ret;
}
static int cmd_test_spilcd(int argc, char **argv)
{
uint8_t ret;
hal_log_info("Run spilcd hal layer test casen");
ret = show_rgb(0);
hal_log_info("spilcd test finishn");
return ret;
}
FINSH_FUNCTION_EXPORT_CMD(cmd_test_spilcd, test_spilcd, spilcd hal APIs tests)
測試命令:
test_spilcd
測試結(jié)果:
1.如果LCD是ARGB8888像素格式輸入,執(zhí)行命令后會顯示一幀紫色畫面。
2.如果LCD是RGB565像素格式輸入,執(zhí)行命令后會顯示一幀藍(lán)色畫面(SPI 協(xié)議是黃色畫面(沒有RB Swap))
FAQ
怎么判斷屏初始化成功
屏初始化成功,一般呈現(xiàn)的現(xiàn)象是雪花屏。因?yàn)槠硫?qū)動里面,在 LCD_open_flow
中添加了lcd_fb_black_screen
的注冊,故正常情況下開機(jī)是有背光的黑屏畫面。
黑屏-無背光
一般是電源或者pwm相關(guān)配置沒有配置好。參考lcd_pwm開頭的相關(guān)配置。
送圖無顯示
排除步驟:
- 首先執(zhí)行spilcd模塊測試命令
test_spilcd
,如果能正常顯示顏色畫面,說明顯示通路正常,只是應(yīng)用未能正確配置送圖接口。如果執(zhí)行測試用例無法正常顯示顏色畫面,接著下面步驟。 - 屏驅(qū)動里面,在
LCD_open_flow
中刪除lcd_fb_black_screen
的注冊,啟動后,如果屏初始化成功應(yīng)該是花屏狀態(tài)(大部分屏如此)。 - 如果屏沒有初始化成功,請檢查屏電源,復(fù)位腳狀態(tài)。
- 如果屏初始化成功,但是發(fā)數(shù)據(jù)時(shí)又沒法顯示,那么需要檢查是不是幀率過快,查看幀率控制。
- 如果電源復(fù)位腳正常,請檢查配置,lcd_dbi_if, lcd_dbi_fmt是否正確,屏是否支持, 如果支持,在屏驅(qū)動里面是否有對應(yīng)上。
- 嘗試修改lcd_dbi_clk_mode。
閃屏
非常有可能是速度跑太快,參考幀率控制一小節(jié)。
畫面偏移
畫面隨著數(shù)據(jù)的發(fā)送偏移越來越大。
嘗試修改lcd_dbi_clk_mode。
屏幕白屏
屏幕白屏,但是背光亮起
白屏是因?yàn)槠聊粵]有初始化,需要檢查屏幕初始化序列或者初始化數(shù)據(jù)是否正確。
屏幕花屏
屏幕花屏,無法控制
花屏一般是因?yàn)槠聊怀跏蓟鬀]有正確設(shè)置 addrwin
,或者初始化序列錯(cuò)誤。
LVGL 屏幕顏色不正確
出現(xiàn)反色,顏色異常
請配置 LVGL LV_COLOR_DEPTH
參數(shù)為 16,LV_COLOR_16_SWAP
為 1,這是由 SPI LCD 的特性決定的。
顯示反色
運(yùn)行 test_spilcd
,屏幕顯示藍(lán)色。
這是由于屏幕啟動了 RB SWAP,一般是 0x36
寄存器修改
正常顯示
sunxi_lcd_cmd_write(sel, 0X36);
sunxi_lcd_para_write(sel, 0x00);
反色顯示
sunxi_lcd_cmd_write(sel, 0X36);
sunxi_lcd_para_write(sel, 0x08);
LVGL 出現(xiàn) DMA OVER SIZE
這是由于 LVGL 配置的 LV_COLOR_DEPTH
為 32,但是 SPI 屏配置為16位。請修改 lv_conf.h
出現(xiàn)部分花屏
- 檢查
address
函數(shù)是否正確 - 檢查
sys_config.fex
屏幕配置分辨率是否正確
總結(jié)
調(diào)試LCD顯示屏實(shí)際上就是調(diào)試發(fā)送端芯片(全志SOC)和接收端芯片(LCD屏上的driver IC)的一個(gè)過程:
- 添加屏驅(qū)動請看編寫屏驅(qū)動
- 仔細(xì)閱讀屏手冊以及driver IC手冊(有的話)。
- 仔細(xì)閱讀板級顯示配置參數(shù)詳解。
- 確保LCD所需要的各路電源管腳正常。
SPI LCD 顏色相關(guān)問題
首先,得先確定顯示屏使用的是SPI接口,還是DBI接口,不同的接口,輸入數(shù)據(jù)的解析方式是不一樣的。
DBI接口的全稱是 Display Bus Serial Interface
,在顯示屏數(shù)據(jù)手冊中,一般會說這是SPI接口,所以有人會誤認(rèn)為SPI屏可以使用 normal spi
去直接驅(qū)動。
閱讀lcd_dbi_if
部分的介紹可以知道,在3線模式時(shí),發(fā)送命令前有1位A0用于指示當(dāng)前發(fā)送的是數(shù)據(jù),還是命令。而命令后面接著的數(shù)據(jù)就沒有這個(gè)A0位了,代表SPI需要在9位和8位之間來回切換,而在讀數(shù)據(jù)時(shí),更是需要延時(shí) dummy clock
才能讀數(shù)據(jù),normal spi
都很難,甚至無法實(shí)現(xiàn)。所以 normal spi
只能模擬4 線的DBI的寫操作。
對于R128這類支持DBI接口的CPU,可以選擇不去了解SPI。如果需要用到SPI去驅(qū)動顯示屏,必須把顯示屏設(shè)置成小端。
RGB565和RGB666
SPI顯示屏一般支持RGB444,RGB565和RGB666,RGB444使用的比較少,所以只討論RGB565和RGB666.
RGB565代表一個(gè)點(diǎn)的顏色由2字節(jié)組成,也就是R(紅色)用5位表示,G(綠色)用6位表示,B(藍(lán)色)用5位表示,如下圖所示:
RGB666一個(gè)點(diǎn)的顏色由3字節(jié)組成,每個(gè)字節(jié)代表一個(gè)顏色,其中每個(gè)字節(jié)的低2位會無視,如下圖所示:
SPI 接口
因?yàn)镾PI接口的通訊效率不高,所以建議使用RGB565的顯示,以 jlt35031c
顯示屏為例,他的顯示驅(qū)動芯片是 ST7789
,設(shè)置顯示格式的方式是往 3a
寄存器寫入0x55(RGB565
)或者 0x66(RGB666)
,在 R128SDK
中,已經(jīng)把 jlt35031c
的通訊格式寫死為 0x55
,lcd_pixel_fmt
配置選項(xiàng)無效:
sunxi_lcd_cmd_write(sel, 0x3a);
sunxi_lcd_para_write(sel, 0x55);
在例程中,輸入的數(shù)據(jù)是 0xff,0x00,0xff,0x00
,對于SPI接口,是按字節(jié)發(fā)送。實(shí)際上,例程只需要每次發(fā)送2字節(jié)即可,因?yàn)榍昂蟀l(fā)送的都是相同的ff 00,所以沒有看出問題。
根據(jù)對 565
的數(shù)據(jù)解析,我們拆分 ff 00
就可以得到紅色分量是 0b11111
,也就是 31
,綠色是0b111000
,也就是 56
,,藍(lán)色是 0
.我們等效轉(zhuǎn)換成 RGB888
,有:
R = 31/31*255 = 255
G = 56/63*255 = 226
在調(diào)色板輸入對應(yīng)顏色,就可以得到黃色
DBI 接口
因?yàn)?DBI
通訊效率較高,所以可以使用 RGB565
或者 RGB666
,使用 DBI
接口,也就是 lcd_if
設(shè)置為1
時(shí),驅(qū)動會根據(jù) lcd_pixel_fmt
配置寄存器,以 SDK
中的 kld2844b.c
為例,這顯示屏的顯示驅(qū)動也是 ST7789
,但是不同的屏幕,廠家封裝時(shí)已經(jīng)限制了通訊方式,所以即使是能使用 DBI 接口的驅(qū)動芯片的屏幕,或許也用不了DBI。
sunxi_lcd_cmd_write(sel, 0x3A); /* Interface Pixel Format */
/* 55----RGB565;66---RGB666 */
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 格式,不再是以字節(jié)的形式去解析,而是以字的方式去解析,為了統(tǒng)一,軟件已經(jīng)規(guī)定了,RGB565
格式時(shí),字大小是2字節(jié),也就是16位,而 RGB666
格式時(shí),字大小是4字節(jié),也就是32位。
對于 RGB565
格式,同樣是設(shè)置為 0xff,0x00
。因?yàn)槠聊皇谴蠖?,而芯片存儲方式是小端,所以芯片?DBI 模塊,會自動把數(shù)據(jù)從新排列,也就是實(shí)際上 DBI 發(fā)送數(shù)據(jù)時(shí),會先發(fā)送0x00
,再發(fā)送0xff
,也就是紅色分量為0,綠色分量為 0b000111
,也就是7,藍(lán)色分量是 0x11111
,也就是31,我們同樣轉(zhuǎn)換成RGB888
G = 7/63*255 = 28
B= 31/31*255 = 255
在調(diào)色板上輸入,可以得到藍(lán)色。
如果是 RGB666
,雖然占用的是3個(gè)字節(jié),但是沒有CPU是3字節(jié)對齊的,所以需要一次性輸入4字節(jié),然后 DBI 硬件模塊,會自動舍棄1個(gè)字節(jié),軟件同意舍棄了最后一個(gè)字節(jié)。
依舊以例程為例,例程輸入了 0xff,0x00,0xff,0x00
,為了方便說明,標(biāo)準(zhǔn)為 0xff(1),0x00(1),0xff(2),0x00(2)
,其中 0x00(2)
會被舍棄掉,然后發(fā)送順序是0xff(2),0x00(1),0xff(1)
,也就是 0xff(2)
是紅色分量,0xff(1)
是藍(lán)色分量,混合可以得到紫色。
-
寄存器
+關(guān)注
關(guān)注
31文章
5295瀏覽量
119836 -
觸發(fā)器
+關(guān)注
關(guān)注
14文章
1995瀏覽量
61013 -
SPI接口
+關(guān)注
關(guān)注
0文章
258瀏覽量
34318 -
LCD顯示
+關(guān)注
關(guān)注
0文章
132瀏覽量
18337 -
顯示驅(qū)動
+關(guān)注
關(guān)注
1文章
63瀏覽量
14947 -
R128
+關(guān)注
關(guān)注
0文章
41瀏覽量
93
發(fā)布評論請先 登錄
相關(guān)推薦
評論