
簡(jiǎn)介本資源是面向嵌入式Linux開(kāi)發(fā)者與Qt初學(xué)者的IMX6ULL平臺(tái)SPI LCD圖形界面實(shí)戰(zhàn)項(xiàng)目聚焦解決ARM Cortex-A7平臺(tái)下ILI9341驅(qū)動(dòng)TFT屏顯示Qt GUI的核心適配問(wèn)題。壓縮包共含多個(gè)關(guān)鍵文件包括適配MSP2402ILI9341的設(shè)備樹(shù)源碼.dts、交叉編譯后的Qt應(yīng)用程序源碼.cpp/.h、構(gòu)建腳本及啟動(dòng)配置整體大小為9.57MB文件類(lèi)型以C源碼、設(shè)備樹(shù)描述和Shell初始化腳本為主支撐從內(nèi)核驅(qū)動(dòng)到應(yīng)用層渲染的完整鏈路。已有278人學(xué)習(xí)下載適合具備基本Linux驅(qū)動(dòng)與Qt開(kāi)發(fā)基礎(chǔ)的學(xué)習(xí)者開(kāi)展實(shí)操驗(yàn)證。讀者可直接部署運(yùn)行掌握SPI總線(xiàn)配置、設(shè)備樹(shù)修改、Qt嵌入式渲染后端如linuxfb適配、以及無(wú)窗口管理器環(huán)境下GUI啟動(dòng)流程等關(guān)鍵技術(shù)點(diǎn)是理解IMX6ULLSPI LCDQt協(xié)同工作的典型工程范例。 最近在做IMX6ULL相關(guān)的東西需要在板子上加一塊SPI接口的TFT小屏來(lái)跑QT界面。屏的型號(hào)是MSP2402驅(qū)動(dòng)IC是經(jīng)典的ILI9341接口是6針SPISCLK、MOSI、CS、DC、RST、BL項(xiàng)目源碼打包下來(lái)是一個(gè)rar名字就叫“IMX6ULL開(kāi)發(fā)板 SPI LCD MSP2402 顯示簡(jiǎn)單的QT界面源碼”。這個(gè)項(xiàng)目看著簡(jiǎn)單但真要把QT、SPI、內(nèi)核fbtft、LCD初始化全家桶湊到一起坑真不少。我干脆把整個(gè)過(guò)程中的方案思路、硬件接線(xiàn)、設(shè)備樹(shù)配置、QT端處理和排障記錄整理出來(lái)給準(zhǔn)備在IMX6ULL這類(lèi)開(kāi)發(fā)板上接SPI小屏跑QT的朋友做個(gè)參考。1. 項(xiàng)目整體設(shè)計(jì)與方案選型1.1 為什么在IMX6ULL上接SPI小屏IMX6ULL跑QT本身毫無(wú)壓力Cortex-A7單核800MHz在240x320這么小的分辨率下QPainter畫(huà)簡(jiǎn)單界面綽綽有余。問(wèn)題在于很多項(xiàng)目并不需要大屏比如儀表盤(pán)選項(xiàng)菜單、設(shè)備配置面板、狀態(tài)指示燈界面一塊2.4寸或者2.8寸的小屏就夠了。SPI接口的屏走線(xiàn)少、占用引腳少在嵌入式產(chǎn)品里很常見(jiàn)。和RGB并口屏相比SPI屏最大的優(yōu)勢(shì)是省引腳。一塊RGB565接口的4.3寸屏需要約20根數(shù)據(jù)線(xiàn)加時(shí)鐘同步線(xiàn)而SPI屏只需要4根數(shù)據(jù)線(xiàn)SCLK、MOSI、CS、DC加上復(fù)位和背光也就6根線(xiàn)對(duì)于IMX6ULL這種引腳不算特別富余的板子非常友好。缺點(diǎn)是刷新率低因?yàn)镾PI是串行傳輸跑滿(mǎn)10MHz時(shí)鐘一幀240x320x2字節(jié)的數(shù)據(jù)大約需要15ms實(shí)際加上指令開(kāi)銷(xiāo)和等待時(shí)間QT局部刷新可以接受但全屏動(dòng)畫(huà)會(huì)有點(diǎn)吃力。在這個(gè)項(xiàng)目里屏是MSP2402驅(qū)動(dòng)IC是ILI9341。ILI9341是市面上最常見(jiàn)的SPI/并口TFT驅(qū)動(dòng)IC之一支持240x320分辨率內(nèi)置GRAM像素格式支持RGB565和RGB666幾乎所有做嵌入式顯示的朋友都見(jiàn)過(guò)這塊IC。MSP2402應(yīng)該是屏模組廠(chǎng)商的型號(hào)通用性沒(méi)問(wèn)題直接按ILI9341處理。1.2 兩條驅(qū)動(dòng)路線(xiàn)fbtft還是用戶(hù)態(tài)SPI打點(diǎn)顯示屏驅(qū)動(dòng)有幾種常見(jiàn)做法我簡(jiǎn)單梳理一下。第一種是內(nèi)核fbtft框架。fbtft是內(nèi)核staging目錄下的一套小屏framebuffer驅(qū)動(dòng)框架專(zhuān)門(mén)用來(lái)支持各種SPI/并口TFT屏。它在內(nèi)核里注冊(cè)一個(gè)獨(dú)立的fb設(shè)備掛上后系統(tǒng)會(huì)多出/dev/fb0或者/dev/fb1用戶(hù)態(tài)程序包括QT只需要往framebuffer里寫(xiě)像素?cái)?shù)據(jù)不用關(guān)心底層SPI時(shí)序。優(yōu)點(diǎn)是QT可以直接以linuxfb的方式工作代碼改動(dòng)最小兼容性好。第二種是用戶(hù)態(tài)直接通過(guò)SPI設(shè)備節(jié)點(diǎn)打點(diǎn)。應(yīng)用層打開(kāi)/dev/spidevX.0通過(guò)ioctl自己發(fā)初始化命令和像素?cái)?shù)據(jù)還要自己維護(hù)一塊顯存。這樣做的缺點(diǎn)是QT沒(méi)辦法直接往spidev里畫(huà)圖除非你自己實(shí)現(xiàn)一個(gè)QPA插件或者自己寫(xiě)一個(gè)繪圖引擎開(kāi)發(fā)量大維護(hù)成本高不推薦做QT界面。第三種是走DRM/KMS這是現(xiàn)代Linux主推的顯示框架但對(duì)SPI小屏的支持很弱而且IMX6ULL的GPU也不強(qiáng)用mipi或者RGB屏才會(huì)考慮走DRM。這個(gè)項(xiàng)目我選fbtft。原因很直接IMX6ULL內(nèi)核自帶fbtft支持ILI9341有現(xiàn)成的fb_ili9341驅(qū)動(dòng)設(shè)備樹(shù)里配好spi節(jié)點(diǎn)注冊(cè)后就能得到一個(gè)240x320的真彩framebufferQT用linuxfb插件直接對(duì)接代碼量最小踩坑最少。1.3 整條顯示鏈路長(zhǎng)什么樣搞清楚數(shù)據(jù)流向后面調(diào)試才不會(huì)懵。IMX6ULL跑Linux內(nèi)核系統(tǒng)起來(lái)后SPI控制器驅(qū)動(dòng)注冊(cè)了SPI總線(xiàn)。fbtft掛在SPI總線(xiàn)上初始化時(shí)通過(guò)SPI向ILI9341發(fā)送初始化序列設(shè)置顯示方向、顏色格式、背光。之后fbtft在內(nèi)核里申請(qǐng)一塊fb內(nèi)存注冊(cè)為fb設(shè)備。QT應(yīng)用通過(guò)linuxfb的QPA插件打開(kāi)/dev/fb0把界面內(nèi)容渲染到fb內(nèi)存fbtft定時(shí)或者按刷新機(jī)制把fb內(nèi)存里的數(shù)據(jù)轉(zhuǎn)換成RGB565像素流通過(guò)SPI逐行發(fā)送給ILI9341ILI9341把數(shù)據(jù)寫(xiě)進(jìn)自己的GRAM完成屏幕顯示。簡(jiǎn)單說(shuō)就是QT繪制 - fbtft的fb內(nèi)存 - SPI - ILI9341 GRAM - 液晶面板。理解了這條鏈路后面遇到白屏、花屏、閃屏就能快速定位是哪個(gè)環(huán)節(jié)出了問(wèn)題。2. 硬件接線(xiàn)與SPI通信細(xì)節(jié)2.1 MSP2402引腳與IMX6ULL接線(xiàn)MSP2402模組上常見(jiàn)的引腳有SCLK、MOSI、CS、DC、RST、BL有些還帶MISO讀GRAM用大多數(shù)場(chǎng)景用不上。IMX6ULL有多個(gè)ECSPI控制器我用ECSPI3引腳分配如下MSP2402引腳功能說(shuō)明IMX6ULL引腳復(fù)用功能SCLKSPI時(shí)鐘UART2_TXD / GPIO1_IO20ECSPI3_SCLKMOSISPI主機(jī)輸出UART2_RXD / GPIO1_IO21ECSPI3_MOSIMISOSPI主機(jī)輸入可空UART2_CTS / GPIO1_IO22ECSPI3_MISOCS片選GPIO1_IO20對(duì)應(yīng)替換實(shí)際用GPIO5_IO11做軟件片選GPIODC數(shù)據(jù)/命令選擇GPIO5_IO14GPIORST復(fù)位GPIO5_IO15GPIOBL背光控制GPIO5_IO13GPIOVCC/GND電源3.3V / GND-這里有個(gè)細(xì)節(jié)要注意CS片選我特意沒(méi)用ECSPI3的硬件片選而是用GPIO模擬。原因是硬件片選在每個(gè)SPI word傳輸時(shí)可能拉高拉低產(chǎn)生毛刺某些屏模組對(duì)CS要求比較嚴(yán)軟件片選更可控。IMX6ULL設(shè)備樹(shù)里cs-gpios屬性可以直接指定GPIO很方便。MISO這根線(xiàn)在純寫(xiě)屏場(chǎng)景下可以不接ILI9341的讀回功能用不到。但建議還是留一根線(xiàn)方便調(diào)試時(shí)讀ID、讀狀態(tài)寄存器特別是排查初始化失敗問(wèn)題時(shí)很管用。2.2 SPI模式、速率和線(xiàn)序的選擇ILI9341支持SPI Mode 0CPOL0CPHA0和Mode 3CPOL1CPHA1兩種模式下時(shí)鐘空閑電平不同。絕大多數(shù)初始化代碼和現(xiàn)成驅(qū)動(dòng)默認(rèn)用Mode 0我在設(shè)備樹(shù)里沒(méi)有額外配spi-cpha和spi-cpol默認(rèn)就是Mode 0。傳輸格式上要注意ILI9341的SPI接口是按字節(jié)傳輸?shù)牟皇前次?。fbtft通過(guò)spi_transfer發(fā)數(shù)據(jù)時(shí)每8個(gè)bit組成一個(gè)字節(jié)DC引腳的電平?jīng)Q定這個(gè)字節(jié)是命令還是數(shù)據(jù)。DC為低發(fā)送命令DC為高發(fā)送數(shù)據(jù)。這個(gè)時(shí)序要求DC信號(hào)先于SCLK建立fbtft驅(qū)動(dòng)內(nèi)部會(huì)在發(fā)送前把DC置好問(wèn)題不大。速率方面ILI9341芯片手冊(cè)上SPI時(shí)鐘上限一般是10MHz但實(shí)際和布線(xiàn)關(guān)系很大。杜邦線(xiàn)連接時(shí)超過(guò)10MHz線(xiàn)間串?dāng)_會(huì)很?chē)?yán)重容易花屏我自己實(shí)測(cè)5MHz最穩(wěn)10MHz在短距離飛線(xiàn)下也能跑。設(shè)備樹(shù)里spi-max-frequency我配的是10000000如果出現(xiàn)花屏再降到5000000或者再低。2.3 ILI9341初始化序列的關(guān)鍵點(diǎn)ILI9341剛上電時(shí)處于未知狀態(tài)必須發(fā)初始化序列。fbtft的fb_ili9341驅(qū)動(dòng)里寫(xiě)好了常規(guī)初始化序列包括軟件復(fù)位、退出睡眠、像素格式設(shè)置、顯示開(kāi)關(guān)等不需要自己寫(xiě)全但有幾個(gè)命令值得理解。命令參數(shù)作用備注0x01無(wú)軟件復(fù)位復(fù)位后要延時(shí)120ms0x11無(wú)退出睡眠模式必須延時(shí)120ms0x360xXX存儲(chǔ)訪(fǎng)問(wèn)控制MADCTL控制掃描方向和RGB/BGR0x3A0x55像素格式0x55即16位RGB5650x20/0x21無(wú)反轉(zhuǎn)顯示開(kāi)關(guān)根據(jù)屏模組選擇部分屏要開(kāi)反轉(zhuǎn)0x29無(wú)打開(kāi)顯示不發(fā)這個(gè)屏幕不亮MADCTL命令0x36很關(guān)鍵它的低3位分別控制顏色格式和掃描方向。BGR位設(shè)為1表示像素?cái)?shù)據(jù)按BGR順序解釋這個(gè)設(shè)錯(cuò)屏上顏色會(huì)明顯偏色比如紅色變成藍(lán)色。項(xiàng)目里MSP2402我實(shí)測(cè)需要設(shè)置BGR0但每個(gè)屏模組可能不一樣如果顏色不對(duì)就改這個(gè)bit。上電時(shí)序上RST引腳要先拉低10ms以上再拉高等待120ms后再發(fā)初始化命令。如果RST懸空或者復(fù)位時(shí)序不對(duì)屏大概率白屏。這個(gè)坑我踩過(guò)后面排障部分細(xì)說(shuō)。3. 內(nèi)核設(shè)備樹(shù)與FBTFT配置3.1 設(shè)備樹(shù)節(jié)點(diǎn)配置示例IMX6ULL的設(shè)備樹(shù)需要配置兩個(gè)大塊一是ECSPI控制器本身的引腳復(fù)用和片選二是掛在SPI總線(xiàn)上的LCD子節(jié)點(diǎn)。我貼出可以直接用的設(shè)備樹(shù)片段iomuxc { pinctrl_ecspi3: ecspi3grp { fsl,pins MX6UL_PAD_UART2_TXD__ECSPI3_SCLK 0x10b0 MX6UL_PAD_UART2_RXD__ECSPI3_MOSI 0x10b0 MX6UL_PAD_UART2_CTS__ECSPI3_MISO 0x10b0 MX6UL_PAD_CSI_DATA05__GPIO5_IO11 0x80000000 /* CS */ MX6UL_PAD_CSI_DATA08__GPIO5_IO14 0x80000000 /* DC */ MX6UL_PAD_CSI_DATA09__GPIO5_IO15 0x80000000 /* RST */ MX6UL_PAD_CSI_DATA07__GPIO5_IO13 0x80000000 /* BL */ ; }; }; ecspi3 { pinctrl-0 pinctrl_ecspi3; fsl,spi-num-chipselects 1; cs-gpios gpio5 11 GPIO_ACTIVE_LOW; status okay; lcd_panel: msp24020 { compatible ilitek,ili9341; reg 0; spi-max-frequency 10000000; rotate 90; fps 30; buswidth 8; dc-gpios gpio5 14 GPIO_ACTIVE_HIGH; reset-gpios gpio5 15 GPIO_ACTIVE_HIGH; led-gpios gpio5 13 GPIO_ACTIVE_HIGH; status okay; }; };這里把CS、DC、RST、BL全部通過(guò)GPIO控制。其中l(wèi)ed-gpios是fbtft的背光控制字段設(shè)置后驅(qū)動(dòng)會(huì)在初始化時(shí)自動(dòng)把背光引腳拉高。如果設(shè)備樹(shù)里不寫(xiě)這個(gè)屏能顯示但背光不亮?xí)屓苏`以為是白屏。3.2 把LCD注冊(cè)成/dev/fb0編譯內(nèi)核時(shí)確認(rèn)開(kāi)啟fbtft和ILI9341驅(qū)動(dòng)。配置路徑在Device Drivers - Staging drivers - Support for small TFT LCD display modules - FB driver for the ILI9341 LCD controller注意fbtft位于staging目錄有些內(nèi)核版本默認(rèn)沒(méi)開(kāi)需要在menuconfig里手動(dòng)勾選。編譯完成后啟動(dòng)系統(tǒng)dmesg里應(yīng)該能看到類(lèi)似內(nèi)容fbtft: module is from the staging directory, the quality is unknown, you have been warned. fb_ili9341: SPI display 240x320 registered as fb0 fbtft: 240x320, SPI, RGB565, 8bit如果看到“registered as fb0”說(shuō)明注冊(cè)成功。沒(méi)有fb0先查設(shè)備樹(shù)有沒(méi)有掛對(duì)再看內(nèi)核日志。啟動(dòng)后可以用cat /sys/class/graphics/fb0/name確認(rèn)設(shè)備節(jié)點(diǎn)名字也可以直接往/dev/fb0里填充數(shù)據(jù)測(cè)試cat /dev/urandom /dev/fb0屏幕出現(xiàn)雪花噪點(diǎn)說(shuō)明顯示鏈路已經(jīng)通了再用QT接管。3.3 調(diào)試顯示參數(shù)的小工具fbtft有一個(gè)很有用的特性很多顯示參數(shù)可以在運(yùn)行時(shí)動(dòng)態(tài)修改不用反復(fù)編譯內(nèi)核和重啟。比如分辨率、虛擬分辨率、旋轉(zhuǎn)方向、灰度gamma在用戶(hù)態(tài)通過(guò)/sys/class/graphics/fb0/目錄下的屬性節(jié)點(diǎn)查看和修改。旋轉(zhuǎn)方向可以通過(guò)fb0的rotate屬性調(diào)整echo 180 /sys/class/graphics/fb0/rotate這個(gè)很實(shí)用。實(shí)際裝屏?xí)r物理方向可能和設(shè)計(jì)方向差90度或者180度一行命令就能轉(zhuǎn)過(guò)來(lái)不用重新編譯。RGB/BGR順序、gamma值也可以在部分fbtft版本里直接調(diào)。調(diào)試顏色順序的快捷方法是在QT里畫(huà)一張純紅、純綠、純藍(lán)的色塊圖看屏上三個(gè)顏色是否對(duì)應(yīng)。如果紅藍(lán)互換就把設(shè)備樹(shù)bgr字段改一下或者調(diào)整MADCTL位。4. QT界面應(yīng)用開(kāi)發(fā)4.1 編譯帶linuxfb插件的QTIMX6ULL上跑QT交叉編譯時(shí)QPA插件至少得有l(wèi)inuxfb。這個(gè)插件是QT基礎(chǔ)平臺(tái)插件在configure時(shí)加上-linuxfb即可。我用的配置命令大致如下./configure -prefix /opt/qt5 \ -xplatform linux-arm-gnueabi-g \ -opensource -confirm-license \ -no-opengl -no-xcb -no-vulkan \ -no-eglfs -no-gtk \ -linuxfb \ -nomake tests -nomake examples \ -no-pch如果希望QT直接全屏鋪滿(mǎn)屏幕不需要桌面系統(tǒng)linuxfb是首選。編譯完成后把QT庫(kù)拷貝到開(kāi)發(fā)板設(shè)置環(huán)境變量export QT_ROOT/opt/qt5 export LD_LIBRARY_PATH$QT_ROOT/lib:$LD_LIBRARY_PATH export QT_QPA_PLATFORMlinuxfb:fb/dev/fb0:size240x320:mmSize90x67 export QT_QPA_FB_FORCE_FULLSCREEN1mmSize這個(gè)參數(shù)建議設(shè)置成接近實(shí)際屏幕物理尺寸的值否則字體縮放可能不對(duì)。如果沒(méi)設(shè)置也沒(méi)關(guān)系QT會(huì)用默認(rèn)值對(duì)界面布局影響不大。有一點(diǎn)要注意QT的linuxfb后端默認(rèn)只做局部刷新它有一個(gè)臟矩形機(jī)制界面變動(dòng)區(qū)域才上報(bào)給framebuffer。這意味著QT不是每幀全屏刷新畫(huà)簡(jiǎn)單靜態(tài)界面時(shí)SPI壓力很小但動(dòng)畫(huà)效果多了刷新率會(huì)明顯下降。4.2 畫(huà)一個(gè)旋轉(zhuǎn)正方體的棱項(xiàng)目源碼里的QT界面包含一個(gè)旋轉(zhuǎn)的正方體這個(gè)效果其實(shí)只用了QPainter沒(méi)有用到OpenGL。IMX6ULL不帶GPUOpenGL只能走軟渲染很吃力所以手動(dòng)做三維投影反而是更靠譜的方案。實(shí)現(xiàn)思路分為幾步。定義8個(gè)頂點(diǎn)坐標(biāo)三維坐標(biāo)范圍在-1到1之間。通過(guò)旋轉(zhuǎn)矩陣?yán)@Y軸或Z軸旋轉(zhuǎn)。最后做正交投影到二維坐標(biāo)再通過(guò)QPainter的drawLine把12條棱畫(huà)出來(lái)。核心代碼思路如下void CubeWidget::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.fillRect(rect(), Qt::black); painter.setPen(QPen(Qt::green, 2)); const float angle m_angle; // 由QTimer定時(shí)遞增 float box[8][3] { {-1,-1,-1},{ 1,-1,-1},{ 1, 1,-1},{-1, 1,-1}, {-1,-1, 1},{ 1,-1, 1},{ 1, 1, 1},{-1, 1, 1} }; QPoint pts[8]; for (int i 0; i 8; i) { float x box[i][0]; float y box[i][1] * cosf(angle) - box[i][2] * sinf(angle); float z box[i][1] * sinf(angle) box[i][2] * cosf(angle); int sx (int)(x * 80 width() / 2); int sy (int)(y * 80 height() / 2); pts[i] QPoint(sx, sy); } static const int edges[12][2] { {0,1},{1,2},{2,3},{3,0}, {4,5},{5,6},{6,7},{7,4}, {0,4},{1,5},{2,6},{3,7} }; for (int i 0; i 12; i) { painter.drawLine(pts[edges[i][0]], pts[edges[i][1]]); } }這個(gè)demo跑在320x240屏上很流暢30fps左右沒(méi)問(wèn)題。關(guān)鍵技巧是QTimer定時(shí)器設(shè)為16ms或者33ms配合update()觸發(fā)重繪視覺(jué)上就是平滑旋轉(zhuǎn)。4.3 中文字體與資源打包SPI小屏跑QT中文字體是個(gè)容易忽略的細(xì)節(jié)。標(biāo)準(zhǔn)QT交叉編譯不帶中文字體直接顯示中文會(huì)變成方框。解決方法是把字體文件拷貝到開(kāi)發(fā)板并在應(yīng)用啟動(dòng)時(shí)加載。常見(jiàn)的做法是把文泉驛微米黑wqy-microhei.ttc或者思源黑體SourceHanSansCN-Regular.otf放到/usr/share/fonts目錄然后代碼里注冊(cè)#include QFontDatabase QFontDatabase::addApplicationFont(/usr/share/fonts/wqy-microhei.ttc); QFont font(WenQuanYi Micro Hei); font.setPointSize(12); app.setFont(font);字體文件比較大幾MB到十幾MB對(duì)于IMX6ULL的eMMC來(lái)說(shuō)完全沒(méi)壓力。如果實(shí)在想省空間可以用Fontmin之類(lèi)的工具裁剪出用到的幾個(gè)漢字幾百KB就搞定。不過(guò)對(duì)大部分項(xiàng)目來(lái)說(shuō)直接放下整個(gè)字體文件最省事調(diào)試也方便。QSS資源文件、圖片資源正常用qrc打進(jìn)二進(jìn)制就行運(yùn)行時(shí)不依賴(lài)額外文件這樣交付方便。唯一要注意的是尺寸大的圖片在SPI刷新下會(huì)有明顯拖影盡量少用大面積漸變和背景圖。4.4 觸摸輸入的接入如果MSP2402模組帶觸摸層QT輸入還需要單獨(dú)配置。電阻觸摸屏在IMX6ULL上通常接SPI或者ADC驅(qū)動(dòng)用ads7846設(shè)備樹(shù)里注冊(cè)觸摸節(jié)點(diǎn)后會(huì)生成一個(gè)輸入設(shè)備事件節(jié)點(diǎn)一般是/dev/input/event0。這時(shí)候QT需要設(shè)置觸摸參數(shù)export QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS/dev/input/event0如果是電容觸摸屏芯片常見(jiàn)的有GT9147、GT911、FT5x06等通過(guò)I2C接口連接設(shè)備樹(shù)里掛好驅(qū)動(dòng)后同樣走evdev輸入事件。電容屏不需要校準(zhǔn)電阻屏必須先跑ts_calibrate生成校準(zhǔn)參數(shù)否則觸摸和顯示坐標(biāo)對(duì)不上。沒(méi)有觸摸時(shí)鼠標(biāo)可以用QT自帶的鼠標(biāo)插件或者通過(guò)tslib模擬。項(xiàng)目源碼里如果只顯示界面不需要交互可以完全不管輸入直接跑純顯示程序。5. 調(diào)試中常見(jiàn)的問(wèn)題與排障實(shí)錄5.1 白屏、暗屏的常見(jiàn)原因白屏是SPI LCD遇到最多的問(wèn)題我的經(jīng)驗(yàn)是先從最簡(jiǎn)單的可能查起。背光不亮導(dǎo)致的“黑屏”很多人誤以為白屏。先看背光GPIO是否被正確拉高最簡(jiǎn)單的方法是用萬(wàn)用表量背光引腳電壓。如果設(shè)備樹(shù)led-gpios沒(méi)寫(xiě)fbtft不會(huì)管背光需要手動(dòng)GPIO控制或者硬件上拉。真正白屏通常意味著ILI9341沒(méi)有執(zhí)行顯示命令。檢查幾點(diǎn)RST引腳是否有正常復(fù)位脈沖復(fù)位后有沒(méi)有延時(shí)足夠久SCLK和MOSI是否接線(xiàn)正確有沒(méi)有接反初始化序列是否完整0x29顯示命令有沒(méi)有發(fā)出去SPI速率是否過(guò)高導(dǎo)致命令傳輸錯(cuò)誤可以先降到1MHz試那次調(diào)MSP2402白屏最后定位到就是設(shè)備樹(shù)里reg屬性沒(méi)寫(xiě)對(duì)導(dǎo)致SPI子設(shè)備匹配不上系統(tǒng)在spidev下創(chuàng)建了/dev/spidev3.0但fbtft沒(méi)有任何反應(yīng)。改好reg后再啟動(dòng)一切正常。5.2 花屏和閃屏排查花屏比白屏好定位至少說(shuō)明屏在工作問(wèn)題基本出在數(shù)據(jù)鏈路。最常見(jiàn)的花屏原因是SPI時(shí)鐘太高。杜邦線(xiàn)連接時(shí)線(xiàn)路電感電容會(huì)把波形拉得很難看10MHz時(shí)鐘下信號(hào)振鈴嚴(yán)重跳變沿不穩(wěn)定花屏非常普遍。解決辦法是把spi-max-frequency降到5MHz或更低或者換成短而粗的飛線(xiàn)有條件的話(huà)直接畫(huà)PCB走線(xiàn)會(huì)好很多。還有一種花屏是數(shù)據(jù)命令切換時(shí)序不對(duì)。DC引腳在傳輸?shù)谝粋€(gè)數(shù)據(jù)bit之前必須已經(jīng)穩(wěn)定如果DC由GPIO控制且切換太慢ILI9341會(huì)把命令當(dāng)數(shù)據(jù)解釋。fbtft內(nèi)部已經(jīng)處理了這個(gè)問(wèn)題如果是自己寫(xiě)用戶(hù)態(tài)驅(qū)動(dòng)這里要注意。閃屏則多半和刷新策略有關(guān)。QT在linuxfb下做部分刷新但如果界面全是大面積變化SPI全屏刷新幀率只有20-30fps視覺(jué)上就會(huì)閃爍。可以試試把fps參數(shù)調(diào)低一點(diǎn)減少刷屏頻率或者減少Q(mào)T動(dòng)畫(huà)幅度。5.3 顏色錯(cuò)亂與鏡像顯示顏色不對(duì)大多數(shù)是BGR和RGB順序問(wèn)題。ILI9341的MADCTL命令里有一個(gè)BGR位針對(duì)不同屏模組0x36設(shè)置的參數(shù)不同。fbtft設(shè)備樹(shù)里可以設(shè)置bgr字段或者在用戶(hù)態(tài)直接改顏色空間配置。我調(diào)試時(shí)先在QT里畫(huà)一幅紅綠藍(lán)三色條看到紅藍(lán)互換就改bgr改完重啟確認(rèn)。鏡像問(wèn)題也是MADCTL的MX、MY位控制的。設(shè)備樹(shù)rotate 90可以整體旋轉(zhuǎn)但個(gè)別方向會(huì)有鏡像。比如rotate 90后左右反了可以再給MADCTL加一個(gè)鏡像位。fbtft里通??梢酝ㄟ^(guò)fb_var_screeninfo的rotate字段或者屏模組廠(chǎng)家的默認(rèn)初始化參數(shù)來(lái)調(diào)和。5.4 SPI信號(hào)質(zhì)量與布線(xiàn)經(jīng)驗(yàn)最后講一點(diǎn)硬件層面的心得。SPI_CLK線(xiàn)上串一個(gè)小電阻是嵌入式里常見(jiàn)的做法一般22Ω左右。這個(gè)電阻能抑制信號(hào)振鈴減少過(guò)沖尤其對(duì)杜邦線(xiàn)連接場(chǎng)景效果明顯。靠近屏端放也可以靠近主控放也可以我習(xí)慣放在主控側(cè)。CS片選線(xiàn)上拉一個(gè)10k電阻到VCC防止控制器復(fù)位時(shí)CS電平不定導(dǎo)致屏幕誤觸發(fā)。如果CS懸空上電瞬間屏可能收到隨機(jī)數(shù)據(jù)進(jìn)入奇怪狀態(tài)。地線(xiàn)一定不要省。MOSI、SCLK、DC、CS全部高速切換參考地回路阻抗高的畫(huà)噪聲會(huì)耦合到信號(hào)線(xiàn)上。我試過(guò)只接一根細(xì)地線(xiàn)花屏概率直線(xiàn)上升接兩根粗地線(xiàn)后問(wèn)題消失。最后再分享一個(gè)實(shí)用小經(jīng)驗(yàn)調(diào)試初期不要直接用QT先在內(nèi)核起來(lái)后用命令行工具測(cè)屏。往/dev/fb0填充純色、畫(huà)幾條對(duì)角線(xiàn)確認(rèn)SPI鏈路完全穩(wěn)定后再啟動(dòng)QT應(yīng)用。這樣一旦出問(wèn)題你能快速知道是QT層的問(wèn)題還是底層驅(qū)動(dòng)的問(wèn)題不會(huì)被一堆報(bào)錯(cuò)帶偏方向。SPI小屏調(diào)試本身不難難的是把各個(gè)環(huán)節(jié)拆開(kāi)獨(dú)立驗(yàn)證這一步做好了后面任何界面問(wèn)題都好解決。本文還有配套的精品資源點(diǎn)擊獲取