上一颗电容芯片配过了。今天换一块屏常见的另一颗:地址、复位脚、速率都要按这颗料来,两颗不要同时打开。

GT911 那篇把 LVDS 电容屏讲透了。手头若换了一块 RGB 并行屏 或某款 MIPI 屏,排线另一头往往不是 GT911,而是 FT5406,地址 0x38,照样挂 I2C2。桌面能点亮、手指划过去没反应,第一步不是去改坐标映射,是先扫总线:0x5d 没了、0x38 在,你就走错芯片了。

两颗芯片不能当「同一种触摸」互相替换配置。地址不同、复位脚不同、中断脚不同、HDF 的 moduleName 不同、上电序列不同。HCS 里只留得下一套 device_touch_chip。GT911 和 FT5406 不要同时 okay——不是 I2C 地址冲突(一个 0x5d、一个 0x38),是复位/中断/芯片驱动只有一份绑定。

下面按「RGB 屏第一次接上」的顺序写:先认芯片,再降 I2C 速度,再配脚,再配 HDF,最后把公版镜像那套 X/Y 对调拿掉。400 kHz 下整总线 NACK 的案子,会单独剖开——它看起来像驱动写错,其实是负载和时序。


1. 先问屏,再问触摸

电容触摸芯片焊在屏的 FPC 上,不焊在底板上。换屏等于换触摸。对应关系在这块 RK3568 设备上非常稳定:

常见触摸I2C 地址INTRST
LVDS 1024×600GT9110x5dGPIO0_C7(23)GPIO2_D0(88)
RGB 并行 1024×600**FT5406****0x38****GPIO3_B2(106)****GPIO3_B1(105)**
某款 MIPIFT54060x38GPIO2_C2(82)GPIO1_A4(36)

数字不要背错组。RGB 那组在 GPIO3,MIPI 那组在 GPIO1/GPIO2,LVDS 的 GT911 在 GPIO0/GPIO2。HCS 只写得下一对 rstGpio/intGpio,写错组的症状是:I2C 地址能扫到(芯片靠硬件上电),但复位脉冲打到另一块屏的空脚上,寄存器读出来是垃圾,或者一直 NACK。

认芯片:

hdc shell "i2cdetect -y -r 2"

RGB 屏接好时,你希望 0x38 是 38 或 UU,0x5d 是 --。反过来就是 GT911 那条屏还插着。两个都在?排线插错座、或者座子上并联了两路 FPC——先把不用的那路拔掉,再谈驱动。

FT5x06 家族的芯片 ID 在寄存器 0xA3(芯片选择)附近,不同料号略有出入。0x38 ACK 已经够你决定走这篇。非要读 ID:

hdc shell "i2cget -y 2 0x38 0xa3"
hdc shell "i2cget -y 2 0x38 0xa8"   # firmware id,视料号

NACK 先别判「没这颗芯片」。往下看第 3 节,400 kHz 会让 已经焊上的芯片 全部 NACK。


2. 内核节点仍然 disabled

和 GT911 一样,开源鸿蒙桌面走 HDF Input。内核主线有 edt-ft5x06compatible = "edt,edt-ft5406")。让它 probe,HDF 就抢不到总线。

&i2c2 {
    status = "okay";
    clock-frequency = <100000>;
    pinctrl-names = "default";
    pinctrl-0 = <&i2c2m1_xfer &ft5x06_int_pin &ft5x06_rst_pin>;

    ft5406_rgb: touchscreen@38 {
        compatible = "edt,edt-ft5406";
        reg = <0x38>;
        interrupt-parent = <&gpio3>;
        interrupts = <RK_PB2 IRQ_TYPE_LEVEL_LOW>;
        reset-gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_LOW>;
        touchscreen-size-x = <1024>;
        touchscreen-size-y = <600>;
        status = "disabled";     /* 让路给 HDF */
    };

    gt911_lvds: gt911@5d {
        compatible = "goodix,gt911";
        reg = <0x5d>;
        status = "disabled";     /* 这块屏不是它 */
    };
};

RGB 脚的 pinctrl:

rgb_panel_tp {
    ft5x06_int_pin: ft5x06-int-pin {
        rockchip,pins = <3 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>;
    };
    ft5x06_rst_pin: ft5x06-rst-pin {
        rockchip,pins = <3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
    };
};

MIPI 那套备件屏把 pinctrl-0 换成:

/* MIPI FT5406:INT=GPIO2_C2, RST=GPIO1_A4 */
pinctrl-0 = <&i2c2m1_xfer &mipi_tp_pins>;
mipi_ft5406_tp {
    mipi_tp_pins: mipi-tp-pins {
        rockchip,pins =
            <1 RK_PA4 RK_FUNC_GPIO &pcfg_pull_none>,
            <2 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>;
    };
};

内核配置:

CONFIG_DRIVERS_HDF_TP_5P43_FT5406=y
# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set

FT5406 的 HDF 驱动和 GT911 的可以同时编进内核(占点空间,切屏不用重编驱动)。真正决定谁 bind 的是 device_info.hcs 的 moduleName 两套都写成 okay 的节点,运行时只会按 device_info 那一条走。


3. 400 kHz:看起来像地址错,其实是总线吃不住

I2C2 上挂的东西不少:触摸 FPC、有时还有相机分支、Type-C 口芯片。排线长、电容大。公版常把 clock-frequency 写成 <400000>。FT5406 在 400 kHz 下的典型日志是:

I2cTransfer: ret = -6

-6 是 ENXIO:主机发了地址,没人 ACK。你会怀疑地址不是 0x38、怀疑 RST 没拉、怀疑供电。把频率改成 100 kHz 再扫,0x38 立刻出现——芯片一直在,只是 400 kHz 的地址帧它咬不住。

&i2c2 {
    clock-frequency = <100000>;
};

这不是「FT5406 规格书只能 100 k」。规格书写到 400 k 的芯片,在长排线、多负载的底板上照样会在 400 k 全灭。降速是工程判断,不是认输。

HCS 里芯片的 maxSpeed 也写成 100,和总线一致:

maxSpeed = 100;    /* 单位 kHz,和 DTS clock-frequency 对齐 */

两边不一致时,HDF 适配层可能按 HCS 再设一次速度。以较低的那档为准,别一边 400 一边 100 来回打。

降速之后 GT911 也能用。I2C 不是「越快越好」的比赛。


4. HCS:RGB 这一套完整配置

vendor/rk/rk3568_evb/hdf_config/khdf/input/input_config.hcs

root {
    input_config {
        touchConfig {
            touch0 {
                boardConfig {
                    match_attr = "touch_device1";
                    inputAttr {
                        inputType = 0;
                        solutionX = 1024;
                        solutionY = 600;
                        devName = "main_touch";
                    }
                    busConfig {
                        busType = 0;
                        busNum = 2;
                    }
                    pinConfig {
                        /* RGB:RST=GPIO3_B1=105,INT=GPIO3_B2=106
                         * MIPI 备件改回 rstGpio=36 intGpio=82
                         */
                        rstGpio = 105;
                        intGpio = 106;
                        rstRegCfg = [0x0, 0x0];
                        intRegCfg = [0x0, 0x0];
                    }
                    powerConfig {
                        vccType = 0;
                        vccNum = 0;
                        vccValue = 1800;
                        vciType = 0;
                        vciNum = 0;
                        vciValue = 3300;
                    }
                }
                chipConfig {
                    template touchChip {
                        match_attr = "";
                        chipName = "ft5406";
                        busType = 0;
                        deviceAddr = 0x38;
                        irqFlag = 2;     /* 低电平 / 下降沿,对齐 DTS LEVEL_LOW */
                        maxSpeed = 100;
                        chipVersion = 0;
                        powerSequence {
                            /* [type, status, dir, delay]
                             * 3=reset 4=int;status 0=low 1=high 2=nop;dir 0=in 1=out
                             *
                             * 正确:INT 保持输入(开漏,主机别推)
                             *       RST 先输出低 20ms,再输出高 300ms 等芯片起来
                             */
                            powerOnSeq = [4, 2, 0, 0,
                                         3, 0, 1, 20,
                                         3, 1, 1, 300];
                            suspendSeq = [3, 0, 2, 10];
                            resumeSeq  = [3, 1, 2, 10];
                            powerOffSeq = [0, 0, 0, 0];
                        }
                    }
                    chip_rgb :: touchChip {
                        match_attr = "ft5406_rgb";
                        chipName = "ft5406";
                        deviceAddr = 0x38;
                    }
                    chip_mipi :: touchChip {
                        match_attr = "ft5406_mipi";
                        chipName = "ft5406";
                        deviceAddr = 0x38;
                    }
                }
            }
        }
    }
}

device_info.hcs

device_touch_chip :: device {
    device0 :: deviceNode {
        policy = 0;
        priority = 130;
        preload = 0;
        permission = 0660;
        moduleName = "HDF_TOUCH_FT5406";
        serviceName = "hdf_touch_ft5406_service";
        deviceMatchAttr = "ft5406_rgb";
    }
}

切回 MIPI 备件屏:deviceMatchAttr 改 ft5406_mipipinConfig 改 36/82,DTS 的 pinctrl 改 MIPI 那组。切回 LVDS+GT911:moduleName 改 HDF_TOUCH_GT911,配置回到 GT911 那篇。一次只亮一条。


5. powerOnSeq 踩过的坑,值得把编码表钉死

HDF 触摸核心按四元组解析上电序列:[type, status, dir, delay]

字段含义
type3操作 RST 脚
type4操作 INT 脚
status0低 / 关
status1高 / 开
status2不动
dir0输入
dir1输出
dir2不改方向

有人写成:

powerOnSeq = [4, 2, 0, 0,
             3, 1, 0, 20,     /* 错:RST HIGH + 输入 */
             3, 1, 1, 300];

3, 1, 0, 20 = RST、HIGH、输入、20 ms。方向是输入时,写 1 只改锁存器,引脚不被驱动。日志里 HandleResetEvent ret=0,你以为复位成功,芯片 RST 脚可能一直被上拉停在高,或者浮空。从未出现过「先拉低」的那条日志。

正确日志应当有两行 RST:

HandleResetEvent: RST gpio=105 dir=1 value=0 delay=20ms  ret=0
HandleResetEvent: RST gpio=105 dir=1 value=1 delay=300ms ret=0

第一行是断言复位(低脉冲),第二行是释放。缺第一行,序列就是错的。

FT5406 的 INT 是开漏,主机不要推它。所以上电时 INT 用 4, 2, 0, 0(nop + 输入)。GT911 有的模组要靠 INT 电平选 I2C 地址,所以上电会先把 INT 当输出推一会儿——那是另一颗芯片的规矩,别抄错。

300 ms 不是随手写的。芯片从 RST 释放到 I2C 可访问,资料上要几百毫秒。你改成 10 ms,偶发 NACK,重现不稳定,最难查。


6. 坐标:公版镜像会左右对调

FT5406 的 HDF 驱动 touch_ft5406.c 里,有的公版分支会强制:

x = solutionX - 1 - rawX;
y = solutionY - 1 - rawY;

这是给某块竖屏模组做的镜像。RGB 1024×600 横屏模组若再做一次,手指往右、光标往左。修法是 ROCKCHIP 分支去掉强制镜像,raw 直接当屏幕坐标,必要时只做一次线性缩放。

/* 横屏 RGB:同向,不做镜像 */
frame->fingers[i].x = rawX * resX / chipMaxX;
frame->fingers[i].y = rawY * resY / chipMaxY;

chipMaxX/Y 以芯片寄存器里读到的分辨率或 HCS 的 solution 为准。若点得准、只是整体偏几毫米,做四边标定,方法和 GT911 那篇相同:慢划四边记 raw 最小最大。

判断「要不要镜像」用事实:手指往右,rawX 增大还是减小。增大 = 同向,不要减。不要看模组丝印上的「左起」「右起」。


7. 切屏时你要改的全部清单

很多人只改 DTS 的 panel 节点,触摸还停在上一块屏的 HCS。清单一次给齐:

  1. 显示 那一路 okay,其余 HDMI/LVDS/MIPI/RGB/eDP disabled(显示总论那篇:同一时刻只喂一路)。
  2. I2C2 pinctrl 换成这块屏的 INT/RST。
  3. HCS pinConfig 的 gpio 编号跟着换。
  4. device_info.hcs 的 moduleName + deviceMatchAttr
  5. solutionX/Y 等于这块屏的像素。MIPI 若是 800×1280,这里就不是 1024×600。
  6. 清 khdf 缓存:touch vendor/rk/rk3568_evb/hdf_config/khdf/hdf.hcs,删 out/kernel/checkpoint/compile_check
  7. 刷 resource(p4)+ boot_linux(p5)。显示时序在 dtb,触摸驱动在内核,HCS 也在内核。漏刷 p4,屏还是旧时序;漏刷 p5,触摸还是旧芯片驱动。

dd 路径仍然是 /dev/block/mmcblkXpY。刷前 cat /proc/partitions


8. 上板验证

hdc shell "i2cdetect -y -r 2"
hdc shell "dmesg | grep -iE 'ft5406|ft5x|HDF_TOUCH|main_touch|I2cTransfer'"
hdc shell "cat /proc/bus/input/devices"

期望:

  • 0x38 有应答或 UU
  • 没有连续 ret = -6
  • ChipDetect / 注册 main_touch 成功
  • /proc/bus/input/devices 里有触摸,Handlers 给出 event 号
hdc shell "od -An -tx1 /dev/input/event11"

手指点下去应有 ABS 事件。四角点桌面图标。

INT 脚确认:

hdc shell "mount -t debugfs none /sys/kernel/debug"
hdc shell "cat /sys/kernel/debug/gpio | grep -E 'gpio-105|gpio-106|gpio-36|gpio-82'"

RGB 应看到 105/106 被 HDF 申请。若 105 写着别的名字,pinctrl 或 hog 抢脚。


9. 完整 C:复位脉冲 + 读 0xA3

用来在 HDF 还没起来时证明「脚和总线是通的」。HDF 已经占用 0x38 时会 busy,那是驱动在干活。

/* ft5406_probe.c
 * armv7-unknown-linux-ohos-clang --target=arm-linux-ohos \
 *   --sysroot=${OHOS_NATIVE}/sysroot \
 *   -o ft5406_probe ft5406_probe.c \
 *   -Wl,--dynamic-linker=/system/lib/ld-musl-arm.so.1
 */
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>

#define ADDR        0x38
#define GPIO_RST    "/sys/class/gpio/gpio105"   /* RGB RST;MIPI 改 gpio36 */
#define GPIO_INT    "/sys/class/gpio/gpio106"

static int wr(const char *path, const char *s)
{
    int fd = open(path, O_WRONLY);
    if (fd < 0) {
        return -1;
    }
    int n = (int)write(fd, s, strlen(s));
    close(fd);
    return n;
}

static int i2c_read_reg(int fd, uint8_t reg, uint8_t *val)
{
    struct i2c_msg msgs[2] = {
        { .addr = ADDR, .flags = 0,        .len = 1, .buf = &reg },
        { .addr = ADDR, .flags = I2C_M_RD, .len = 1, .buf = val },
    };
    struct i2c_rdwr_ioctl_data x = { .msgs = msgs, .nmsgs = 2 };
    return ioctl(fd, I2C_RDWR, &x);
}

int main(void)
{
    wr("/sys/class/gpio/export", "105");
    wr("/sys/class/gpio/export", "106");
    wr(GPIO_RST "/direction", "out");
    wr(GPIO_INT "/direction", "in");

    wr(GPIO_RST "/value", "0");
    usleep(20 * 1000);
    wr(GPIO_RST "/value", "1");
    usleep(300 * 1000);

    int fd = open("/dev/i2c-2", O_RDWR);
    if (fd < 0) {
        perror("open i2c-2");
        return 1;
    }
    uint8_t id = 0;
    if (i2c_read_reg(fd, 0xa3, &id) < 0) {
        perror("read 0xA3");
        printf("若 ENXIO:先把 I2C2 降到 100kHz 再试。\n");
        close(fd);
        return 2;
    }
    close(fd);
    printf("FT5x chip id @0xA3 = 0x%02x\n", id);
    return 0;
}

GPIO 已被 HDF 申请时,export 会失败。那就把程序当成「驱动没起来时的急救包」,不要和 HDF 抢。


10. ArkTS 探针(换屏不用改应用)

触摸进 MMI 之后,应用不关心芯片型号。GT911 那篇的 TouchProbe.ets 原样可用。应用层坐标错,先看 HCS 的 solution 和驱动映射;应用层完全没事件,先看前面的 dmesg / evtest,别在 ArkTS 里加 I2C。

权限:系统桌面不需要你的 HAP 声明触摸权限。你自己的页面 onTouch 就能收到。


11. 不要和 GT911 同时 okay——展开说一次

「地址不同就能共存」在 I2C 理论上成立。这里不成立的原因有四条:

  1. HCS 只有一个 device_touch_chip 子节点,moduleName 只能写一个。
  2. pinConfig 只有一对 RST/INT。两颗芯片的脚不在同一组 GPIO。
  3. powerOnSeq 不同。GT911 要推 INT 选地址,FT5406 要求主机别推 INT。
  4. 内核若把 goodix 和 edt-ft5x06 都 okay,两条主线再加 HDF,三条抢 I2C2。

正确做法是 一份镜像对应一块屏。仓库里可以同时留两套片段,用注释或 overlay 切换,不要运行时两套都 okay。


12. 排障表

现象先看原因处理
0x38 全 NACK`clock-frequency`400 kHz 负载太大降到 100 kHz
0x38 ACK,寄存器 NACKRST 日志有没有 value=0powerOnSeq 方向写成输入改成 `3,0,1,20` 再 `3,1,1,300`
ChipDetect 失败moduleName / 脚号还绑着 GT911,或脚是 MIPI 那组改 device_info + pinConfig
有触摸,左右反touch_ft5406.c 镜像公版竖屏分支去掉 `res-raw`
点得准但差一块屏的偏移solution 800×1280 vs 1024×600切屏没改 HCS对齐像素
改 HCS 无效hdf.hcs mtimeinclude 缓存touch 顶层 hcs,清 checkpoint
和 GT911 谁也不稳两套都 okay抢总线抢脚只留一套
flowchart TD
  A[RGB/MIPI 屏无触摸] --> B{i2cdetect 0x38?}
  B -->|无| C{400kHz?}
  C -->|是| D[降到 100kHz]
  C -->|否| E[排线 供电 座子]
  B -->|有| F{RST 日志有低脉冲?}
  F -->|无| G[改 powerOnSeq]
  F -->|是| H{device_info 是 FT5406?}
  H -->|否| I[还在 GT911 配置]
  H -->|是| J{脚是 105/106 还是 36/82?}
  J --> K[对齐这块屏]
  K --> L{坐标左右对?}
  L -->|否| M[去掉公版镜像]

FT5406 @0x38,常跟 RGB / 某款 MIPI 走。LVDS 那条是 GT911,别混。I2C2 降到 100 kHz——400 kHz 的 NACK 不是「地址写错」的铁证。内核 edt-ft5x06 保持 disabled。HDF 的 moduleName、脚、序列、solution 四件套一起切。powerOnSeq 的 dir 写错时,复位日志仍返回 0,要看有没有「先拉低」。和 GT911 不要同时 okay。切屏等于切触摸配置,然后刷 p4 和 p5。

离开触摸之后,板载那三颗按键会骗你一次:KEY3 的名字不是 SARADC 通道 3。


系列第 23 篇 · 芯片:瑞芯微 RK3568 · OpenHarmony 4.1(API 11) · Linux 5.10

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐