Lithiumee

Lithiumee

Go wild

重新利用回收的智能手機顯示器

背景#

最近想要給一個嵌入式項目尋找一塊 5 寸左右、1080P 分辨率且帶觸摸的 MIPI 屏幕。在淘寶搜索一圈,發現一款比較合適且便宜的 5.5 寸屏幕模組大約 95 元一片。為了進一步壓縮成本,就開始琢磨有沒有再便宜一點的方案,於是開始思考能否利用手機的屏幕。在全面屏時代之前,5.5 英寸、1080P 是智能手機的主流配置,應當有相當多的選擇,由於存量大,屏幕模組價格可能也具有優勢。長久以來都看著舊手機的屏幕眼饞,這些屏幕雖然來自過時的設備,但顯示素質往往遠超一般的嵌入式屏幕模組,要是能利用起來就好了。

不過也不是每個手機的屏幕都能輕易拿來使用,點亮手機屏幕的核心障礙在於信息缺失。不像單獨出售的成品屏幕模組一般會直接提供規格書和初始化資料,手機屏幕往往沒有公開的軟硬件資料。要驅動手機屏幕,必須解決兩大難題:

一是硬件上需要知道屏幕連接器的型號和引腳定義,才能正確連接電源和信號。雖然理論上可以從顯示控制 IC 密密麻麻的引腳一點一點追蹤線路到連接器,有大佬能夠這麼操作,但工程量相當浩大。更便捷的途徑是獲取手機的維修原理圖,得益於龐大的手機維修市場,許多機型的原理圖都可以在網絡上或一些維修工具上找到。從原理圖中可以直接查到屏幕連接器的接口定義,甚至連接器型號。

另一方面是軟件上需要知道屏幕的初始化序列與時序。這個一般可以從手機的固件中提取,對於高通平台的設備,可以在設備樹中獲得時序和初始化序列(可看雲犀 Box 文章的設備樹提取部分)。這個流程不需要真的擁有一個設備,只要有刷機包一般便可以進行提取。而有一部分特殊的設備,受到開源社區的支持,如比較活躍的項目 postmarketOS。對於這樣的設備,我們可以直接從開源代碼倉庫中獲取所需屏幕信息。

所以我決定找軟柿子下手,看看是否能找到一款擁有合適屏幕,並且同時滿足硬件有圖紙、軟件有源碼的設備。經過在 postmarketOS 的支持設備列表中尋找,最終鎖定了小米 5X (xiaomi-tissot) 和紅米 5Plus (xiaomi-vince),它們的屏幕模組在淘寶的售價已經低到了 40 元附近。我也對兩款屏幕做了對比:

小米 5X 的屏幕為 5.5 英寸,1080x1920 (16:9),403 PPI,68% NTSC 色域。其模組採用的是 GFF 全貼合,使用光學膠將觸控層向上層玻璃蓋板進行貼合,工藝簡單但厚度厚且視覺效果稍差,不點亮屏幕時也覺得黑得不純粹。
image
紅米 5Plus 的屏幕為 5.99 英寸,1080x2160 (18:9),403 PPI,84% NTSC 色域。同樣的屏幕還被用在小米 6X、紅米 5Plus 上。其模組採用了 Incell 全貼合工藝,觸控單元向下和 LCM 融合了,模組更薄且視覺效果很通透。
image 1
由於我根本不需要安卓的三個導航鍵,以差不多的模組尺寸換取更大的顯示面積並獲得一定程度的 “全面屏” 效果是十分誘人的,紅米 5Plus 無疑是更優的選擇。但我計劃使用的 RV1126 SoC 在數據手冊中標稱支持的最高分辨率為 1920x1080,能否驅動 2160x1080 的屏幕存在疑問。因此我購買了兩種屏幕,計劃都進行測試,若紅米 5Plus 的屏幕不能完美兼容,則只能退而求其次選擇小米 5X 的屏幕作為保底。

硬件#

分析原理圖後發現,小米 5X(上)與紅米 5Plus(下)的屏幕接口定義十分接近,但是所用連接器則不同。小米 5X 使用的為 BM23PF0.8-40DS-0.35V (880),引腳間距 0.35mm;紅米 5Plus 使用的則為 BM20B (0.8)-40DS-0.4V (51),間距 0.4mm,兩者引腳數相同但間距不同,互不兼容。
image 2
image 3
根據原理圖,我製作了一塊轉接板,其一方面做信號轉接,將屏幕的 MIPI-DSI 信號、I2C 信號及其他控制 IO,轉接至與易百納 EB-RV1126-DC-201 開發板相匹配的排座上。另一方面板載所需的電源電路,利用開發板提供的 3.3V 電源,生成背光所需的高電壓、LCD 所需的正負電壓,以及開發板未直接提供的 1.8V IO 電壓
image 4
IMG_20250416_165032
轉接板焊接完畢,兩塊屏幕接好轉接板,至此硬件就準備就緒,可以開始在軟件上操作點屏幕了。

IMG_20250416_163046_2
IMG_20250416_162431

軟件#

Rockchip 提供的 Rockchip_DRM_Panel_Porting_Guide 說明了點 MIPI-DSI 屏幕的一般操作。它利用了 Linux 內核中的 panel-simple-dsi 驅動,對於一般的屏幕,可以免去單獨添加驅動的麻煩,僅通過修改設備樹就可異完成驅動。

配置 panel-simple-dsi 需要提供兩大核心信息:

  • 初始化序列 (panel-init-sequence):包含了一系列初始化命令,其每條命令的規則為:每條命令由頭部和數據負載 (Payload) 構成,都以 16 進制表示。頭部包含 3 個字節,分別定義了數據類型 (Data Type)、發送後延時 (Delay, ms) 和負載長度 (Payload Length)。從第四個字節開始的數據代表長度為 Payload Length 的負載。其中的 Data Type 有這些種類:
    • 0x05: DCS Short WRITE. no parameters;
    • 0x15: DCS Short WRITE.1 parameter;
    • 0x39: DCS Long Write/write LUT Command ;
    • 0x03: Generic Short WRlTE, no parameters;
    • 0x13: Generic Short WRITE.1 parameter;
    • 0x23: Generic Short WRITE,2 parameters;
    • 0x29: Generic Long Write;
  • 顯示時序 (display-timings):定義了屏幕刷新過程中的各項時間參數,需要完整知道下面的每個部分
    • Htotal = Hactive + Hfront-porch+ HSync + Hback-porch
    • Vtotal = Vactive + Vfront-porch + VSync + Vback-porch
    • pixel-clock = Htotal × Vtotal × 幀率

對於有較好社區支持的小米 5X 和紅米 5Plus,可以從 msm8953-mainline 項目的 lk2nd 設備樹中找到屏幕兼容的驅動名稱,內核代碼中找到具體的屏幕驅動。

例如,對於小米 5X,可以在lk2nd/dts/msm8953-xiaomi-vince.dts at main · msm8953-mainline/lk2nd這個設備樹裡找到使用的屏幕和對應兼容驅動。實際上像下面所列,同一型號手機可能涉及多種供應商的屏幕,屏幕驅動 IC 和觸摸方案可能都不相同,因此有多個條目。正常來講,需要根據屏幕模組的 LCD_ID 引腳,或者通過 MIPI-DSI 讀特定寄存器來判斷屏幕方案,進行不同的初始化。但本次僅作一塊屏幕的測試,就選擇了最簡單人工的逐一嘗試。

	panel {
		compatible = "xiaomi,vince-panel";
		qcom,mdss_dsi_td4310_fhdplus_video_e7 {
			compatible = "xiaomi,td4310-fhdplus-e7";
			touchscreen-compatible = "syna,rmi4-i2c";
			// touchscreen-compatible = "novatek,nt36525-i2c";
		};
		// ...
		qcom,mdss_dsi_nt36672_csot_fhdplus_video_e7 {
			compatible = "xiaomi,nt36672-csot-fhdplus-e7";
			touchscreen-compatible = "syna,rmi4-i2c";
			// touchscreen-compatible = "novatek,nt36525-i2c";
		};
	};

以其中一款 xiaomi,nt36672-tianma-fhdplus-e7 為例,對應驅動代碼則在linux/drivers/gpu/drm/panel/msm8953-generated/panel-xiaomi-nt36672-tianma-fhdplus-e7.c at 6.12/main · msm8953-mainline/linux,要做的就是提取代碼中的信息,按正確格式填入 panel-simple-dsi 對應的設備樹節點內。填寫完成後長這樣:

&dsi {
	status = "okay";
	rockchip,lane-rate = <1000>;
	dsi_panel: panel@0 {
		status = "okay";
		compatible = "simple-panel-dsi";
		reg = <0>;
		reset-gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_LOW>;
		pinctrl-names = "default";
		pinctrl-0 = <&dsi_rst_gpio>;
		backlight = <&backlight>;
		
		reset-delay-ms = <200>;
		enable-delay-ms = <100>;
		prepare-delay-ms = <20>;
		unprepare-delay-ms = <20>;
		disable-delay-ms = <20>;
		init-delay-ms = <120>;
		
		dsi,flags = <(MIPI_DSI_MODE_VIDEO |MIPI_DSI_MODE_VIDEO_BURST |MIPI_DSI_MODE_LPM |MIPI_DSI_MODE_EOT_PACKET)>;
		dsi,format = <MIPI_DSI_FMT_RGB888>;
		dsi,lanes  = <4>;
		/* Based on panel-xiaomi-nt36672-tianma-fhdplus-e7.c/nt36672_tianmaplus_e7_on() function */
		panel-init-sequence = [
			// DCS Short Write
			05 78 01 11
			// Generic Short Write
			13 00 02 B0 04
			// Generic Short Write
			13 00 02 D6 01
			// Generic Long Write
			29 00 27 C7 00 19 28 3b 4a 55 6d 7d 8a 96 48 54 62 76 7f 8b 99 a4 b2 00 19 28 3b 4a 55 6d 7d 8a 96 48 54 62 76 7f 8b 99 a4 b2
			// Generic Long Write
			29 00 38 C8 03 00 01 03 ff fe 00 00 fe 01 fd f7 00 00 01 ff fb f2 00 00 01 03 01 ec 00 00 fe 01 fd f5 00 00 01 fe fa fe 00 00 01 03 ff fe 00 00 fe 01 fd ec 00 00 fe 01 fb d3 00
			// DCS Long Write
			39 00 03 51 ff 00
			// DCS Short Write
			15 00 02 53 24
			// DCS Short Write
			15 00 02 55 00
			//DCS Short Write
			15 00 02 35 00
			// DCS Short Write
			05 14 01 29
		];

		/* panel-exit-sequence */
		panel-exit-sequence = [
			05 14 01 28
			05 78 01 10
		];

		
		disp_timings1: display-timings {
			native-mode = <&dsi_timing0>;
			dsi_timing0: timing0 {
				clock-frequency = <148500000>;// (480+50+60+10)*(800+20+34+2)*60
				
				hactive = <1080>;
				hfront-porch = <108>;
				hback-porch = <12>;
				hsync-len = <60>;

				vactive = <1920>;
				vfront-porch = <166>;
				vback-porch = <84>;
				vsync-len = <33>;
				
				hsync-active = <0>;
				vsync-active = <0>;
				de-active = <0>;
				pixelclk-active = <0>;
				
				swap-rb = <0>;
				swap-rg = <0>;
				swap-gb = <0>;
			};
		};
		
	// ...
	
};

此外,背光升壓 IC 需要一個 PWM 信號作為使能和調光控制。硬件上已經連接好,只需多在設備樹中配置一個 pwm-backlight 通用驅動節點,上面的 dsi 節點也已經引用了這個背光節點。

	backlight: backlight {
		compatible = "pwm-backlight";
		brightness-levels = <0 1 2 3 ... 255>;
		default-brightness-level = <200>;
		pwms = <&pwm7 0 25000 0>; // PWM7, 25kHz
	};

測試#

燒錄好帶新設備樹的內核鏡像,進入終端之後,可以使用modetest -M rockchip -s 83@69:1080x1920進行測試。

點屏幕最怕一片漆黑,畢竟軟件沒寫對、硬件沒接對都會導向同樣的一片漆黑。很不幸,初次測試時嘗試了多種初始化序列,屏幕還是一片漆黑,毫無反應。Rockchip_DRM_Panel_Porting_Guide 也提供了一種排查方法,可以通過 MIPI-DSI 的讀操作來判斷通信鏈路是否正常。將讀命令添加到 panel-simple 驅動當中,即可在啟動時觀察讀取是否報錯和 mode 是否有變化。測試發現只有持續用力按壓排線到 PCB 的排座上時才能正確讀取,原來是焊接不牢的低級錯誤。這類排座連接器間距密,引腳焊盤非常小,可能也有焊盤和阻焊開窗偏小的原因,導致上錫都比較困難,反復嘗試才勉強焊接牢靠。排除此問題後,屏幕便非常順利地直接點亮了。
image 5

最終效果#

小米 5X 的屏幕可以正常顯示,測試畫面和播放視頻都正常,達到了預期效果。
IMG_20250416_164734
IMG_20250416_164818
IMG_20250416_164917
IMG_20250416_164836
不出意外,紅米 5Plus 的屏幕由於 RV1126 的限制不能完美顯示。若在設備樹中直接配置 vactive 到 2160,驅動在 probe 階段有長寬與硬件最大能力進行比較的邏輯,超過最大能力 1920 便導致無法初始化。即使移除該判斷邏輯,繞過檢查強行初始化,則顯示內容完全錯亂。一種勉強可用的方法是將 vactive 縮小到硬件支持的 1920,並把多餘 240 放到時序其他部分。這樣,屏幕可以正常顯示 1080x1920 的區域,但有 1080x240 無法正常顯示。
IMG_20250416_163849
IMG_20250416_163921
IMG_20250416_164053
IMG_20250416_132620

結語#

通過這次經歷可以發現,點亮手機屏幕有時也並不困難,前提是有軟硬件資料的支撐(無論是網絡分享的還是自己逆向的)。在手機屏幕基本都使用通用 MIPI-DSI 協議的情況下,絕大多數擁有維修原理圖和開放固件的手機屏幕,都具備被二次利用的巨大潛力,用於業餘的嵌入式項目是可行且經濟的方案。

一個有趣的例子是手機維修行業的 “萬能 “屏幕測試儀,靠更換不同的排線和配置可以兼容海量移動設備屏幕的測試。比較好奇的是他們是如何獲取大量設備的時序和初始化序列?是搞清楚驅動 IC 型號然後套用公版數據,還是通過邏輯分析儀抓取並逆向 DSI 的低速初始化序列呢。
image 6
最後,本次使用的屏幕畢竟是來自近 8 年前的產品,如今的手機屏幕已有長足進步,即使還是像紅米 Note 11T Pro 這樣的 LCD 屏,也屏佔比更高,有高刷,色域更廣,更不用說更強的 OLED 了,希望之後能挑戰一些更新款手機的屏幕。此外,另一個值得我研究的是如何在嵌入式 Linux 環境下,利用 ArgyllCMS 等開源工具對屏幕進行校色,從而在項目中也能實現一定程度的專業色彩管理,使呈現在這些屏幕上的內容也能夠色彩準確。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。