戰(zhàn):TaoToken統(tǒng)一Key打通嵌入式輸入鏈路)
1. QT/E 觸摸屏從驅(qū)動(dòng)到應(yīng)用層為什么總斷鏈Q(jìng)T/E 觸摸屏支持這件事說穿了就是把一條鏈路打通觸摸面板產(chǎn)生中斷內(nèi)核 input 子系統(tǒng)上報(bào)事件tslib 做校準(zhǔn)和濾波QT/E 的鼠標(biāo)協(xié)議插件讀取設(shè)備節(jié)點(diǎn)最后 QWS 或 EGLFS 把坐標(biāo)投遞給你的 QWidget。這條鏈上任何一環(huán)斷了表現(xiàn)都是「點(diǎn)了沒反應(yīng)」或者「點(diǎn)左邊跳右邊」。工控 HMI 和手持終端開發(fā)者最常遇到的就是驅(qū)動(dòng)明明加載了、cat /dev/input/event0也有數(shù)據(jù)但 QT 程序里就是不動(dòng)。我在一塊 i.MX6 工控板上調(diào)這塊花了整整兩天。現(xiàn)象是命令行用ts_test畫線完全正常一跑./app -qws就失靈。后來發(fā)現(xiàn)是 QWS_MOUSE_PROTO 指向了/dev/h3600_tsraw這個(gè)默認(rèn)名而我的板子實(shí)際節(jié)點(diǎn)是/dev/input/event1。QT/E 的 LinuxTp 插件在編譯時(shí)把設(shè)備路徑寫死了不改源碼或者不通過環(huán)境變量覆蓋它永遠(yuǎn)找不到正確的節(jié)點(diǎn)。這條鏈路涉及幾個(gè)關(guān)鍵概念先對齊一下。tslib 是觸摸屏的抽象層負(fù)責(zé)把原始 ADC 值通過pointercal校準(zhǔn)文件換算成屏幕像素坐標(biāo)同時(shí)做去抖和濾波。QT/E 的鼠標(biāo)協(xié)議插件qmouselinuxtp_qws.cpp對應(yīng) LinuxTpqmouselinuxinput_qws.cpp對應(yīng) LinuxInput負(fù)責(zé)從 tslib 或直接 from evdev 讀事件。QWS 是 QT/E 自帶的窗口系統(tǒng)EGLFS 是 QT5 之后基于 OpenGL ES 的全屏平臺(tái)插件兩者讀取觸摸參數(shù)的路徑不一樣。適合讀這篇的人正在做 QT/E 嵌入式 HMI、手持終端、醫(yī)療設(shè)備面板的開發(fā)者已經(jīng)能交叉編譯 QT/E 庫但觸摸沒調(diào)通的以及從 QT4 QWS 遷移到 QT5 EGLFS 時(shí)觸摸配置對不上的。下面我按「先定位問題、再給可復(fù)制配置、最后驗(yàn)證和排障」的順序?qū)懨恳徊蕉寄苤苯映侥愕陌遄由吓?。核心檢索詞先明確QT/E 觸摸屏支持、tslib 校準(zhǔn)配置、QWS_MOUSE_PROTO、EGLFS 觸摸參數(shù)、pointercal 文件、觸摸坐標(biāo)偏移排查。這些詞你搜到的多數(shù)文章只講一半我把驅(qū)動(dòng)到應(yīng)用層整條鏈補(bǔ)齊。2. TaoToken 統(tǒng)一 Key 在嵌入式輸入鏈路調(diào)試?yán)锏那爸脺?zhǔn)備調(diào)試觸摸鏈路時(shí)我經(jīng)常需要查 QT/E 源碼里鼠標(biāo)插件的實(shí)現(xiàn)、對比不同版本的qmouselinuxtp_qws.cpp差異或者讓模型幫我分析evtest的原始輸出格式。這些查詢?nèi)绻看味家袚Q不同的 API Key 和模型入口效率很低。TaoToken 在這里的作用是提供一個(gè)統(tǒng)一的 Key把模型對話、代碼補(bǔ)全、文檔檢索收斂到一個(gè)入口省掉在多個(gè)平臺(tái)之間倒騰憑證的時(shí)間。先說清楚它是什么TaoToken 是一個(gè)大模型 API 聚合服務(wù)你用同一個(gè) Key 就能調(diào)用不同廠商的模型。對嵌入式開發(fā)者來說實(shí)際價(jià)值在于調(diào)試時(shí)可以讓模型讀你的evtest輸出、幫你寫 tslib 的校準(zhǔn)腳本、解釋 QT/E 源碼里的坐標(biāo)變換邏輯而不用為每個(gè)模型單獨(dú)申請和配置 Key。它適合誰需要頻繁查文檔、讀源碼、寫測試腳本又不想在憑證管理上花時(shí)間的開發(fā)者。前置準(zhǔn)備分三步。第一步拿到 Key。訪問 API Keys 管理頁https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite登錄后創(chuàng)建一個(gè)新 Key復(fù)制保存。這個(gè) Key 后面在環(huán)境變量或配置文件里用。第二步確認(rèn)你的調(diào)用方式。如果你是在開發(fā)機(jī)上用命令行工具查資料可以直接用 curl 測通如果你是在 IDE 里用插件比如 Cline、Continue需要在插件設(shè)置里填 Base URL 和 Key。Base URL 是https://taotoken.net/api注意這個(gè)地址不帶 UTM 參數(shù)直接寫就行。第三步選模型。調(diào)試觸摸鏈路時(shí)我一般用擅長讀代碼和長文本的模型來分析源碼用響應(yīng)快的模型做日常查詢。你可以在模型對話頁https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite看當(dāng)前可用的模型列表記下你要用的 Model ID后面配置里要填。這里有個(gè)坑要提前說TaoToken 是 API 服務(wù)不是編輯器插件本身也不是代理工具。它的作用是讓你用一個(gè) Key 調(diào)用模型你的 QT/E 代碼、tslib 配置、設(shè)備節(jié)點(diǎn)這些還是在你本地板子和開發(fā)機(jī)上操作。別指望它替你編譯 QT 庫或者燒寫文件系統(tǒng)。如果你打算長期在嵌入式項(xiàng)目里用模型輔助編碼和調(diào)試可以了解 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite它面向的是持續(xù)編碼和 Agent 場景比單次調(diào)用更適合日常開發(fā)流。但這一節(jié)的重點(diǎn)是先把 Key 拿到、Base URL 記好、Model ID 選好后面配置觸摸鏈路時(shí)隨時(shí)能查資料。3. tslib 校準(zhǔn)與 QWS/EGLFS 觸摸參數(shù)的可復(fù)制配置這一節(jié)是全文的核心我給出一套可以直接抄的配置。分三塊tslib 校準(zhǔn)、QWS 環(huán)境變量、EGLFS 參數(shù)。每塊都給出文件路徑和完整內(nèi)容。3.1 tslib 校準(zhǔn)與 pointercal 文件生成tslib 的校準(zhǔn)文件叫pointercal默認(rèn)放在/etc/pointercal。它是一行 7 個(gè)數(shù)字格式是a0 a1 a2 a3 a4 a5 a6前 6 個(gè)是坐標(biāo)變換系數(shù)第 7 個(gè)是縮放因子。你不需要手算用ts_calibrate生成即可。前提是你的板子上已經(jīng)編譯并安裝了 tslib且TSLIB_TSDEVICE指向正確的設(shè)備節(jié)點(diǎn)。先確認(rèn)設(shè)備節(jié)點(diǎn)ls /dev/input/ cat /proc/bus/input/devices找到你的觸摸屏對應(yīng)的 event 節(jié)點(diǎn)比如/dev/input/event1。然后設(shè)置環(huán)境變量并運(yùn)行校準(zhǔn)export TSLIB_TSDEVICE/dev/input/event1 export TSLIB_CALIBFILE/etc/pointercal export TSLIB_CONFFILE/etc/ts.conf export TSLIB_PLUGINDIR/usr/lib/ts ts_calibrate屏幕上會(huì)出現(xiàn)十字光標(biāo)依次點(diǎn)擊五個(gè)點(diǎn)。完成后/etc/pointercal就生成了。如果提示open /dev/input/event1: No such file or directory說明節(jié)點(diǎn)名不對回到cat /proc/bus/input/devices重新確認(rèn)。/etc/ts.conf的內(nèi)容也要對常見配置如下module_raw input module pthres pmin1 module variance delta30 module dejitter delta100 module linearmodule_raw input表示從 Linux input 子系統(tǒng)讀原始事件。如果你用的是老式 ADC 觸摸屏可能是module_raw h3600之類按你的驅(qū)動(dòng)來。module linear負(fù)責(zé)用 pointercal 做線性變換這行必須有。3.2 QWS 環(huán)境變量配置QT/E 在 QWS 模式下觸摸協(xié)議通過QWS_MOUSE_PROTO指定。格式是協(xié)議名:設(shè)備節(jié)點(diǎn)。如果你用 tslib協(xié)議名是Tslib如果直接用 Linux input協(xié)議名是LinuxInput。在板子的啟動(dòng)腳本里比如/etc/profile或你的應(yīng)用啟動(dòng)腳本加上export QTDIR/usr/local/qt-e export QWS_MOUSE_PROTOTslib:/dev/input/event1 export QWS_DISPLAYLinuxFb:/dev/fb0 export TSLIB_TSDEVICE/dev/input/event1 export TSLIB_CALIBFILE/etc/pointercal export TSLIB_CONFFILE/etc/ts.conf export TSLIB_PLUGINDIR/usr/lib/ts export LD_LIBRARY_PATH$QTDIR/lib:/usr/lib/ts:$LD_LIBRARY_PATH注意QWS_MOUSE_PROTO里的設(shè)備節(jié)點(diǎn)要和TSLIB_TSDEVICE一致。如果你在編譯 QT/E 時(shí)改了qmouselinuxtp_qws.cpp里的默認(rèn)路徑這里的環(huán)境變量會(huì)覆蓋它。如果你用的是 QT5 的 EGLFSQWS_MOUSE_PROTO 不再生效改用QT_QPA_EGLFS_TSLIB或QT_QPA_EGLFS_INTEGRATION。下一小節(jié)說。3.3 EGLFS 觸摸參數(shù)配置QT5 之后EGLFS 平臺(tái)插件讀取觸摸的方式變了。你需要設(shè)置export QT_QPA_PLATFORMeglfs export QT_QPA_EGLFS_INTEGRATIONeglfs_integration export QT_QPA_EGLFS_TSLIB1 export QT_QPA_EGLFS_PHYSICAL_WIDTH154 export QT_QPA_EGLFS_PHYSICAL_HEIGHT86 export TSLIB_TSDEVICE/dev/input/event1 export TSLIB_CALIBFILE/etc/pointercal export TSLIB_CONFFILE/etc/ts.confQT_QPA_EGLFS_TSLIB1告訴 EGLFS 用 tslib 讀觸摸。物理寬高按你屏幕的實(shí)際毫米數(shù)填影響的是 DPI 計(jì)算不影響觸摸坐標(biāo)但填錯(cuò)會(huì)導(dǎo)致界面元素縮放異常。如果你不用 tslib直接用 libinput可以設(shè)export QT_QPA_EGLFS_INTEGRATIONeglfs_kms export QT_QPA_EGLFS_KMS_CONFIG/etc/qt-eglfs-kms.json然后在 JSON 里配觸摸設(shè)備。不過工控場景下 tslib 更常見因?yàn)樾?zhǔn)文件好管理。3.4 設(shè)備節(jié)點(diǎn)映射的 udev 規(guī)則板子重啟后 event 節(jié)點(diǎn)號(hào)可能變導(dǎo)致配置失效。用 udev 規(guī)則固定節(jié)點(diǎn)名# /etc/udev/rules.d/99-touchscreen.rules SUBSYSTEMinput, ATTRS{name}Goodix-TS, SYMLINKinput/touchscreen這樣無論 event 號(hào)怎么變/dev/input/touchscreen始終指向你的觸摸屏。然后把所有配置里的/dev/input/event1換成/dev/input/touchscreen。ATTRS{name}的值從cat /proc/bus/input/devices里的Name拿。4. 觸摸坐標(biāo)驗(yàn)證與多點(diǎn)觸控回歸測試配置寫完怎么確認(rèn)真的通了分三步驗(yàn)證原始事件、tslib 層、QT 應(yīng)用層。4.1 用 evtest 看原始事件evtest /dev/input/event1手指點(diǎn)屏幕你應(yīng)該看到類似輸出Event: time 1690000000.123456, type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value 0 Event: time 1690000000.123456, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 400 Event: time 1690000000.123456, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 300 Event: time 1690000000.123456, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 1如果這里沒輸出問題在驅(qū)動(dòng)層跟 QT 無關(guān)。檢查設(shè)備樹里觸摸屏的 interrupt 和 reset 引腳配置。4.2 用 ts_test 驗(yàn)證 tslib 層export TSLIB_TSDEVICE/dev/input/event1 ts_test屏幕上會(huì)出現(xiàn)一個(gè)十字你手指移動(dòng)時(shí)十字跟著動(dòng)。如果十字不動(dòng)或者亂跳檢查/etc/ts.conf里的module_raw和module linear是否加載。如果坐標(biāo)偏移重新跑ts_calibrate。4.3 QT 應(yīng)用層驗(yàn)證寫一個(gè)最小 QT 程序打印觸摸坐標(biāo)#include QApplication #include QWidget #include QMouseEvent #include QDebug class TouchWidget : public QWidget { protected: void mousePressEvent(QMouseEvent *e) override { qDebug() press: e-pos() global: e-globalPos(); } void mouseMoveEvent(QMouseEvent *e) override { qDebug() move: e-pos(); } }; int main(int argc, char **argv) { QApplication app(argc, argv); TouchWidget w; w.resize(800, 480); w.show(); return app.exec(); }編譯后運(yùn)行./touch_test -qws點(diǎn)屏幕終端應(yīng)該打印坐標(biāo)。如果沒打印檢查QWS_MOUSE_PROTO是否設(shè)置、設(shè)備節(jié)點(diǎn)是否存在、LD_LIBRARY_PATH是否包含 tslib 庫。4.4 多點(diǎn)觸控回歸測試如果你的觸摸屏支持多點(diǎn)用evtest看 ABS_MT_SLOT 和 ABS_MT_TRACKING_ID 事件。QT/E 的 QWS 對多點(diǎn)支持有限QT5 的 EGLFS 配合 libinput 支持更好。測試動(dòng)作# 同時(shí)用兩根手指點(diǎn)屏幕觀察 evtest 輸出 evtest /dev/input/event1 | grep -E ABS_MT_SLOT|ABS_MT_TRACKING_ID|ABS_MT_POSITION如果只有單點(diǎn)事件說明驅(qū)動(dòng)沒上報(bào)多點(diǎn)檢查設(shè)備樹里touchscreen-max-points或類似屬性?;貧w測試腳本可以寫成#!/bin/bash # touch_regression.sh DEV/dev/input/event1 echo 開始觸摸回歸測試請依次點(diǎn)擊屏幕四角和中點(diǎn) for i in 1 2 3 4 5; do echo 請點(diǎn)擊第 $i 個(gè)點(diǎn)... timeout 10 evtest $DEV | head -20 echo --- done5. 本篇常見錯(cuò)排查401、local proxy failed、reading choices、OAuth調(diào)試觸摸鏈路時(shí)如果你同時(shí)用模型輔助查資料可能會(huì)遇到 API 調(diào)用報(bào)錯(cuò)。這些錯(cuò)和觸摸本身無關(guān)但會(huì)打斷你的調(diào)試節(jié)奏這里一并列出來。5.1 401 Unauthorized報(bào)錯(cuò)原文{error:{message:Invalid API key provided,type:invalid_request_error}}原因Key 填錯(cuò)、過期、或者復(fù)制時(shí)帶了空格。檢查你的環(huán)境變量或配置文件里的 Key確保沒有多余字符。如果你在 Cline 或 Continue 里配置注意 Base URL 要填https://taotoken.net/api不要多寫路徑。5.2 local proxy failed報(bào)錯(cuò)原文Error: local proxy failed: dial tcp 127.0.0.1:7890: connect: connection refused原因你的開發(fā)機(jī)上配了本地代理但代理服務(wù)沒啟動(dòng)。嵌入式開發(fā)機(jī)經(jīng)常為了拉依賴配代理后來關(guān)了但環(huán)境變量還在。檢查http_proxy和https_proxyenv | grep -i proxy unset http_proxy https_proxy all_proxy然后重試。注意這里說的是開發(fā)機(jī)上的本地代理配置問題不是讓你去用什么網(wǎng)絡(luò)工具只是清理殘留的環(huán)境變量。5.3 reading choices 報(bào)錯(cuò)報(bào)錯(cuò)原文Error: reading choices: unexpected end of JSON input原因模型返回的 JSON 被截?cái)嗤ǔJ?max_tokens 設(shè)太小或者網(wǎng)絡(luò)中斷。如果你在讓模型分析長源碼把 max_tokens 調(diào)大或者分段發(fā)送。在 Cline 里可以在設(shè)置里調(diào)Max Tokens。5.4 OAuth 相關(guān)報(bào)錯(cuò)報(bào)錯(cuò)原文Error: OAuth token expired, please re-authenticate原因如果你用的是需要 OAuth 的客戶端比如某些 IDE 插件token 過期了。重新走一遍授權(quán)流程或者在插件設(shè)置里換成 API Key 方式。TaoToken 用的是 API Key不涉及 OAuth所以如果你在插件里看到這個(gè)錯(cuò)說明插件還在用舊的認(rèn)證方式改成填 Base URL Key 即可。5.5 CC Switch / Cline MCP / Codex auth.json 三件套如果你用 CC Switch 管理多個(gè)模型配置或者用 Cline 的 MCP 功能或者用 Codex 的 auth.json記住三件套必須齊全Base URL、Key、Model ID。CC Switch 的配置示例{ name: taotoken, baseUrl: https://taotoken.net/api, apiKey: sk-你的Key, model: claude-3-5-sonnet-20241022 }Cline 的 MCP 配置在cline_mcp_settings.json{ mcpServers: { taotoken: { url: https://taotoken.net/api, apiKey: sk-你的Key } } }Codex 的auth.json{ apiKey: sk-你的Key, baseUrl: https://taotoken.net/api, model: gpt-4o }三件套缺一不可。只填 Key 不填 Base URL會(huì)走默認(rèn)端點(diǎn)只填 Base URL 不填 Model ID會(huì)報(bào)模型不存在。5.6 觸摸鏈路本身的常見錯(cuò)回到觸摸。如果你遇到open /dev/input/event1: Permission denied檢查設(shè)備節(jié)點(diǎn)權(quán)限ls -l /dev/input/event1 chmod 666 /dev/input/event1或者把你的應(yīng)用加到input組。如果 QT 程序報(bào)QWSMouseHandler: Cannot open mouse device檢查QWS_MOUSE_PROTO格式冒號(hào)前后不能有空格。如果坐標(biāo)偏移但ts_test正常檢查 QT 的屏幕分辨率是否和觸摸屏分辨率一致。QWS 下用-display參數(shù)指定EGLFS 下檢查QT_QPA_EGLFS_PHYSICAL_WIDTH/HEIGHT。6. 把統(tǒng)一 Key 接進(jìn)你的嵌入式調(diào)試流觸摸鏈路調(diào)通之后日常開發(fā)里還有很多重復(fù)勞動(dòng)讀 QT/E 源碼找坐標(biāo)變換函數(shù)、寫 tslib 校準(zhǔn)的自動(dòng)化腳本、分析 evtest 日志、對比不同板子的設(shè)備樹配置。這些事如果每次都要手動(dòng)翻文檔很耗時(shí)間。我的做法是把 TaoToken 的 Key 配到開發(fā)機(jī)的環(huán)境變量里然后在終端里用 curl 快速查詢export TAOTOKEN_KEYsk-你的Key curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_KEY \ -H Content-Type: application/json \ -d { model: claude-3-5-sonnet-20241022, messages: [{role:user,content:解釋 QT/E qmouselinuxtp_qws.cpp 里坐標(biāo)變換的流程}] }這樣在調(diào)試板子的時(shí)候不用切瀏覽器就能查源碼邏輯。如果你在 IDE 里寫 QT 代碼把 Key 配到 Cline 或 Continue 里寫觸摸事件處理時(shí)可以直接讓模型補(bǔ)全。對于長期做嵌入式 HMI 的Coding Plan 更適合因?yàn)樗嫦虺掷m(xù)編碼場景不用每次單獨(dú)調(diào)用。你可以從接入文檔https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite看具體的接入方式文檔里有各語言的示例。最后給一個(gè)實(shí)用技巧把觸摸調(diào)試的常用命令寫成腳本配合模型查詢形成固定流程。比如#!/bin/bash # touch_debug.sh echo 設(shè)備節(jié)點(diǎn) cat /proc/bus/input/devices | grep -A5 -i touch echo tslib 校準(zhǔn)文件 cat /etc/pointercal 2/dev/null || echo pointercal 不存在 echo 環(huán)境變量 env | grep -E TSLIB|QWS_MOUSE|QT_QPA echo 原始事件測試5秒 timeout 5 evtest /dev/input/event1 | head -30每次板子出問題先跑這個(gè)腳本把輸出貼給模型分析比你自己一行行查快得多。觸摸鏈路的問題無非就是節(jié)點(diǎn)、權(quán)限、校準(zhǔn)、協(xié)議四類腳本跑完基本能定位到哪一類。