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