Rockchip RK3399 - USB触摸屏接口驱动
目录
----------------------------------------------------------------------------------------------------------------------------
开发板 :NanoPC-T4
开发板
eMMC
:16GB
LPDDR3
:4GB
显示屏 :15.6
英寸HDMI
接口显示屏
u-boot
:2023.04
linux
:6.3
----------------------------------------------------------------------------------------------------------------------------
一、触摸屏接口分类
触摸屏主要包括电阻触摸屏和电容触摸屏,这个我们在《linux
驱动移植-LCD
触摸屏设备驱动》中已经详细介绍了,这里不再重复介绍。
在《linux
驱动移植-LCD
触摸屏设备驱动》这篇文章中我们介绍了SoC
S3C2440
触摸屏驱动的实现,对于S3C2440
来说,其只支持四线电阻触摸屏。在测试时我们使用的LCD
型号为LCD-T35(TD035STEB4)
,其4线连接在S3C2440
的AIN4~AIN7
引脚上,通过AIN4~AIN7
引脚来接收模拟输入信号。
在驱动程序中,我们通过接收触摸屏按下/松开中断,在中断处理程序中获取ADC
采样值,计算X/Y
坐标,并上报坐标、点击等事件。
当前市面上使用的触摸屏一般都使用了I2C
接口,当然也有SPI
、USB
等接口。同时支持多点触摸,那什么是多点触摸呢?
顾名思义,多点触摸技术指的是允许用户同时通过多个手指来控制图形界面的一种技术,与多点触摸技术相对应的当然就是单点触摸。
1.1 I2C
接口触摸屏
首先,需要了解I2C
触摸屏的工作原理。I2C
触摸屏是一种通过I2C
接口连接到SoC
的输入设备,它的工作原理类似于普通的触摸屏。一般来言,I2C触摸屏
内部驱动板都会有一个触摸IC
,比如FT5426
;
- 此芯片一端连接触摸屏的模拟信号,对触摸动作采样然后
AD
转换; - 另一端通过
I2C
连接SoC
,即将AD
转换后的数据通过I2C
接口发给SoC
;
对于I2C
接口触摸屏来说:
-
所谓的触摸驱动本质上就是
I2C
设备驱动; -
触摸
IC
提供了中断信号引脚,当检测到触摸信息后就会触发中断,那么就要在中断处理程序里来读取触摸信息;得到的是触摸位置绝对信息以及触摸屏是否有按下;
1.2 USB
接口触摸屏
现在我手里有一个NanoPC-T4
开发板,同时还有一个15.6
英寸HDMI
接口显示屏,并且该显示屏支持Type-C
十点触摸。因此我的目标,就是移植USB
触摸屏驱动到内核6.3
版本上。
首先,需要了解usb
触摸屏的工作原理。USB
触摸屏是一种通过USB
接口连接到SoC
的输入设备,它的工作原理类似于普通的触摸屏。一般来言,USB
触摸屏内部驱动板都会有一个USB
芯片;
- 此芯片一端连接触摸屏的模拟信号,对触摸动作采样然后AD转换;
- 另一端通过
USB
连接SoC
,即将AD
转换后的数据通过USB
接口发给SoC
;
在linux
内核中,USB HID transport layer
驱动程序实现了USB
接口的HID
设备,其中包括USB
接口的keyboards
(键盘)、mice
(鼠标)、joysticks
(摇杆)、graphic tablets
(绘图板)、触摸屏等其他的。具体来说,它会通过USB
总线获取来自触摸屏的数据,并将其转换为标准的输入事件(例如按键、鼠标移动等),然后将其传递给系统。
二、多点触摸
2.1 多点触摸协议
在linux
内核中有一份详细的文档介绍了多点电容触摸协议,位置在Documentation/input/multi-touch-protocol.rst
,多点触摸协议又简称MT
协议,MT
协议又分为:
TypeA
:适用于触摸点不能被区分或者跟踪触摸设备,此类型的设备上报原始数据,这种类型用得比较少;TypeB
:适用于触摸点能够被硬件追踪并区分触摸点的触摸设备,两次相同的触摸数据不上报,而是缓存在slot
对象中,且通过slot
更新某一个触摸点的信息;FT5626
就属于才类型,一般的多点触摸IC
都有此能力。
2.2 多点触摸事件
触摸点的信息是通过一系列ABS_MT
事件上报给linux
内核,只有ABS_MT
事件用于多点触摸,ABS_MT
事件定义在文件include/uapi/linux/input-event-codes.h
中,相关定义为:
/*
* 0x2e is reserved and should not be used in input drivers.
* It was used by HID as ABS_MISC+6 and userspace needs to detect if
* the next ABS_* event is correct or is just ABS_MISC + n.
* We define here ABS_RESERVED so userspace can rely on it and detect
* the situation described above.
*/
#define ABS_RESERVED 0x2e
#define ABS_MT_SLOT 0x2f /* MT slot being modified */
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
#define ABS_MT_POSITION_X 0x35 /* Center X touch position */
#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */
#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */
#define ABS_MT_TOOL_X 0x3c /* Center X tool position */
#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
其中:
ABS_MT_SLOT
:上报触摸点ID
;ABS_MT_TRACKING_ID
:为触摸点分配ID
,用于轨迹跟踪;ABS_MT_POSITION_X
:上报触摸点X
轴坐标信息;ABS_MT_POSITION_Y
:上报触摸点Y
轴坐标信息;ABS_MT_TOUCH_MAJOR
:上报触摸区域长轴信息(触点椭圆形);ABS_MT_WIDTH_MAJOR
:上报触摸区域短轴信息(触点椭圆形);
2.2.1 TYPE A
TYPE A
上报方式:
ABS_MT_POSITION_X x[0] // 第一个点X轴坐标
ABS_MT_POSITION_Y y[0] // 第一个点Y轴坐标
SYN_MT_REPORT // 点与点之间使用
ABS_MT_POSITION_X x[1] // 第二个点X轴坐标
ABS_MT_POSITION_Y y[1] // 第二个点Y轴坐标
SYN_MT_REPORT // 点与点之间使用
SYN_REPORT //同步事件
(1) 上报触摸点的X
轴坐标和Y
轴坐标,通过 input_report_abs
函数来完成,此函数原型如下所示:
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}
(2) 对于TYPE A
类型的设备,通过input_mt_sync
函数来隔离不同的触摸点数据信息,此函数原型如下所示:
tatic inline void input_mt_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
}
此函数只有1个参数,类型为input_dev
,用于指定具体的input_dev
设备,input_mt_sync
函数会触发同步事件SYN_MT_REPORT
事件码,此事件会通知接收者获取当前触摸数据,并且准备接收下一个触摸点数据。
(3) 当所有的触摸点坐标都上传完毕以后就得发送SYN_REPORT
事件,使用input_sync
函数来完成;
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
2.2.2 TYPE B
TYPE B
上报方式:
ABS_MT_SLOT 0 // 上报触摸点序号
ABS_MT_TRACKING_ID 45 // 为触摸点分配ID
ABS_MT_POSITION_X x[0] // 上报触摸点X轴坐标信息
ABS_MT_POSITION_Y y[0] // 上报触摸点Y轴坐标信息
ABS_MT_SLOT 1 // 以下同上
ABS_MT_TRACKING_ID 46
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_REPORT // 同步事件
对于协议B,内核驱动应该把每一个识别出的触摸点和一个slot
相关联,使用slot
来传播触摸状态的改变,通过修改关联slot
的ABS_MT_TRACKING_ID
来达到对触摸点的创建,替换和销毁。
ABS_MT_TRACKING_ID
用来跟踪触摸点属于哪一条线,如果触摸点的ID
值与上一次事件中ID
值相等,那么他们就属于同一条线,ID
值并不是随便赋值的,而是硬件上跟踪了触摸点的轨迹,比如按下一个点硬件会跟踪这个点的ID
,只要不抬起上报的点都会和这个ID
相关;- 上报
ABS_MT_TRACKING_ID -1
系统会清除对应的ID
和slot
;
(1) 对于TYPE B
类型的设备,上报触摸点信息的时候需要通过input_mt_slot
函数来区分是哪一个触摸点,此函数原型如下所示:
static inline void input_mt_slot(struct input_dev *dev, int slot)
{
input_event(dev, EV_ABS, ABS_MT_SLOT, slot);
}
此函数只有2个参数:
- 第一个参数为
input_dev
设备; - 第二个参数
slot
用于指定当前上报的是哪个触摸点的信息;比如10点触摸,触摸设备最多可以同时支持10个触摸点,slot
可以理解为是第几个触摸点;
input_mt_slot
函数会触发绝对位移事件ABS_MT_SLOT
事件码,此事件会告诉接收者当前正在更新的是哪个触摸点(slot
)的数据。
(2) 根据Type B
的要求,每个slot
必须关联一个ABS_MT_TRACKING_ID
,通过修改slot
关联的 ABS_MT_TRACKING_ID
来完成对触摸点的添加、替换或删除。具体用到的函数就是input_mt_report_slot_state
;
bool input_mt_report_slot_state(struct input_dev *dev,
unsigned int tool_type, bool active);
其中第3个参数,active
包括:
-
true
:连续触摸,内核会自动分配一个ABS_MT_TRACKING_ID
给slot
; -
false
:触摸点抬起,表示某个触摸点无效了,内核会分配一个-1
给slot
。
三、测试
本节实验是在《Rockchip RK3399
-USB
调试》文章的基础上进行的,因此需要先参考这篇文章进行内核和设备树的配置。
3.1 配置设备树
由于我们的触摸屏为Type C
接口,而我们使用的开发板NanoPC-T4
只有1个USB3.0 Type-C
接口,因此我们需要将这个接口配置为HOST
(主机)。
USB3.0 Type-C
接口对应的USB
控制器为USB3.0 OTG0(DWC3/xHCI)
、对应的USB PHY
为USB3.0 Type-C PHY0
和USB2.0 OTG PHY0
;
因此对应的设备树配置,包括USB3.0 OTG0(DWC3/xHCI)
控制器设备树配置和USB3.0 Type-C PHY
0、USB2.0 OTG PHY0
设备树配置。
3.1.1 控制器配置
(1) USB3.0 OTG0(DWC3/xHCI)
控制器设备节点usbdrd3_0
,定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi
;
usbdrd3_0: usb@fe800000 {
compatible = "rockchip,rk3399-dwc3";
#address-cells = <2>;
#size-cells = <2>;
ranges;
clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>,
<&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_RKSOC_AXI_PERF>,
<&cru ACLK_USB3>, <&cru ACLK_USB3_GRF>;
clock-names = "ref_clk", "suspend_clk",
"bus_clk", "aclk_usb3_rksoc_axi_perf",
"aclk_usb3", "grf_clk";
resets = <&cru SRST_A_USB3_OTG0>;
reset-names = "usb3-otg";
status = "disabled";
usbdrd_dwc3_0: usb@fe800000 {
compatible = "snps,dwc3";
reg = <0x0 0xfe800000 0x0 0x100000>;
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH 0>;
clocks = <&cru SCLK_USB3OTG0_REF>, <&cru ACLK_USB3OTG0>,
<&cru SCLK_USB3OTG0_SUSPEND>;
clock-names = "ref", "bus_early", "suspend";
dr_mode = "otg";
phys = <&u2phy0_otg>, <&tcphy0_usb3>;
phy-names = "usb2-phy", "usb3-phy";
phy_type = "utmi_wide";
snps,dis_enblslpm_quirk;
snps,dis-u2-freeclk-exists-quirk;
snps,dis_u2_susphy_quirk;
snps,dis-del-phy-power-chg-quirk;
snps,dis-tx-ipgap-linecheck-quirk;
power-domains = <&power RK3399_PD_USB3>;
status = "disabled";
};
};
3.1.2 PHY
配置
(1) USB3.0 Type-C PHY0
设备节点tcphy0_usb3
,定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi
;
tcphy0: phy@ff7c0000 {
compatible = "rockchip,rk3399-typec-phy";
reg = <0x0 0xff7c0000 0x0 0x40000>;
clocks = <&cru SCLK_UPHY0_TCPDCORE>,
<&cru SCLK_UPHY0_TCPDPHY_REF>;
clock-names = "tcpdcore", "tcpdphy-ref";
assigned-clocks = <&cru SCLK_UPHY0_TCPDCORE>;
assigned-clock-rates = <50000000>;
power-domains = <&power RK3399_PD_TCPD0>;
resets = <&cru SRST_UPHY0>,
<&cru SRST_UPHY0_PIPE_L00>,
<&cru SRST_P_UPHY0_TCPHY>;
reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
rockchip,grf = <&grf>;
status = "disabled";
tcphy0_dp: dp-port {
#phy-cells = <0>;
};
tcphy0_usb3: usb3-port {
#phy-cells = <0>;
};
};
(2) USB2.0 OTG PHY0
设备节点u2phy0_otg
,定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi
;
u2phy0: usb2phy@e450 {
compatible = "rockchip,rk3399-usb2phy";
reg = <0xe450 0x10>;
clocks = <&cru SCLK_USB2PHY0_REF>;
clock-names = "phyclk";
#clock-cells = <0>;
clock-output-names = "clk_usbphy0_480m";
status = "disabled";
u2phy0_host: host-port {
#phy-cells = <0>;
interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH 0>;
interrupt-names = "linestate";
status = "disabled";
};
u2phy0_otg: otg-port {
#phy-cells = <0>;
interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH 0>,
<GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH 0>,
<GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH 0>;
interrupt-names = "otg-bvalid", "otg-id",
"linestate";
status = "disabled";
};
};
3.1.3 电源配置
(1) USB2.0 PHY0
芯片三路电源依次为VCCA0V9_S3
、VCCA1V8_S3
、VCC3V3_S3
;
在arch/arm64/boot/dts/rockchip/rk3399-evb.dts
配置vcc3v3_s3
设备节点;
&i2c0 {
......
rk808: pmic@1b {
......
regulators {
......
vcc3v3_s3: SWITCH_REG1 {
regulator-always-on;
regulator-boot-on;
regulator-name = "vcc3v3_s3";
regulator-state-mem {
regulator-off-in-suspend;
};
};
......
};
};
};
完整的rk808
设备节点配置参考:rk808
驱动配置。
(2) USB3.0 Type-C PHY0
芯片三路电源和USB2.0 PHY0
芯片三路电源一样。
(3) USB3.0 Type-C
接口电源VBUS_TYPEC
由RT9724GQW
提供的,其输入端为VCC5V0_SYS
。由GPIO4_D2
引脚使能,高电平有效;
在arch/arm64/boot/dts/rockchip/rk3399-evb.dts
配置vbus_typec
;
vcc5v0_sys: vcc5v0-sys {
compatible = "regulator-fixed";
regulator-name = "vcc5v0_sys";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
};
vbus_typec: vbus-typec {
compatible = "regulator-fixed";
enable-active-high;
gpio = <&gpio4 RK_PD2 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&vbus_typec_en>;
regulator-name = "vbus_typec";
vin-supply = <&vcc5v0_sys>;
};
由于vbus_typec
节点配置了default
状态对应的引脚配置节点为vbus_typec_en
,因此需要在pinctrl
节点下增加引脚配置节点vbus_typec_en
,这个节点在fusb
配置中介绍。
3.1.4 使能
下面介绍的都是配置在arch/arm64/boot/dts/rockchip/rk3399-evb.dts
文件。
(1) 使能usbdrd3_0
:
通过dr_mode
属性设置USB
控制器的模式:
/* Configurate and Enable USB3.0/2.0 OTG Controller notifier */
&usbdrd3_0 {
status = "okay";
};
&usbdrd_dwc3_0 {
/* 配置dr_mode为host */
dr_mode = "host";
status = "okay";
};
(2) 使能tcphy0_usb3
;
/* Enable USB3.0 PHY */
&tcphy0 {
status = "okay";
};
&tcphy0_usb3{
status = "okay";
};
(3) 使能u2phy0_otg
,同时配置USB2.0 OTG PHY0 phy-supply
属性,用于控制USB
电源VBUS
;
/* Enable USB2.0 PHY */
&u2phy0 {
status = "okay";
};
&u2phy0_otg {
phy-supply = <&vbus_typec>;
status = "okay";
};
3.2 编译内核
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了