当前位置: 首页 > news >正文

37.2多点电容触摸屏实验(详细代码)_csdn

根据前面知识类博客总结可知,咱们这一内容涉及:
1、I2C驱动框架;
2、中断框架;
3、input 子系统框架;
4、linux 的 MT 协议;
以下内容知识如果涉及以前知识,不会详细讲解。
触摸屏原理图一共涉及到 4 个引脚,如下所示:
Pasted image 20250918095945.png
一共有4个引脚:
分别是:I2C2_SCL(PH4)、I2C2_SDA(PH5)、CT_RST(PH15)、CT_INT(PI1)。
我们进行实验驱动的是FT5426 设备;

1、修改镜像和设备树

1.1、 添加 FT5426 设备节点

1、添加引脚节点

FT5426 用到了 I2C2 接口以及 PI1 和 PH15 这两个 IO;
首先关于I2C2本文涉及的接口PH4和PH5:
在stm32mp15- pinctrl.dtsi 文件,添加如下引脚配置信息:

i2c2_pins_a: i2c2-0 {pins {pinmux = <STM32_PINMUX('H', 4, AF4)>, /* I2C2_SCL */<STM32_PINMUX('H', 5, AF4)>; /* I2C2_SDA */bias-disable;drive-open-drain;slew-rate = <0>;};};i2c2_pins_sleep_a: i2c2-1 {pins {pinmux = <STM32_PINMUX('H', 4, ANALOG)>, /* I2C2_SCL */<STM32_PINMUX('H', 5, ANALOG)>; /* I2C2_SDA */};};i2c2_pins_b1: i2c2-2 {pins {pinmux = <STM32_PINMUX('H', 5, AF4)>; /* I2C2_SDA */bias-disable;drive-open-drain;slew-rate = <0>;};};i2c2_pins_sleep_b1: i2c2-3 {pins {pinmux = <STM32_PINMUX('H', 5, ANALOG)>; /* I2C2_SDA */};};

其次另外两个本文涉及的接口PI1和PH15:
在stm32mp15- pinctrl.dtsi 文件,添加如下引脚配置信息:

	ft5426int_reset_pins_a: ft5426int_reset_pins-0 {pins1 {pinmux = <STM32_PINMUX('I', 1, GPIO)>, /* ft5426 INT */<STM32_PINMUX('H', 15, GPIO)>; /* ft5426 RESET */bias-pull-up;slew-rate = <0>;};};

编译:

make uImage LOADADDR=0XC2000040 -j8 //编译内核

复制给开发板:

sudo cp arch/arm/boot/uImage /home/chensir/linux/tfboot -f

2、 FT5426 节点配置

Pasted image 20250918110637.png
在stm32mp157d- atk.dts 文件,追加I2C2关于FT5426信息;

&i2c2 {
pinctrl-names = "default", "sleep";pinctrl-0 = <&i2c2_pins_a>;
pinctrl-1 = <&i2c2_pins_sleep_a>;
status = "okay";ft5426: ft5426@38 {compatible = "edt,edt-ft5426";pinctrl-0 = <&ft5426int_reset_pins_a>;reg = <0x38>;irq-gpios = <&gpioi 1 GPIO_ACTIVE_LOW>;reset-gpios = <&gpioh 15 GPIO_ACTIVE_LOW>;status = "okay";};
};

注意:本人在这里栽了一个礼拜以上!
我买的正点原子MP157触摸屏是7寸的,网上和文档大部分说7寸是ft系列,但是我把触摸屏拆下来发现这个是goodixGt911系列!!!
然后用原子给的gt9147的代码可以驱动,但是写进内核没法,大概需要认真写时序,需要把module注册换成late_initcall形式才可以。
module_i2c_driver 宏,这个宏默认会让驱动以 module_init 优先级 注册,但编译为内置(obj-y)时,module_init 会被内核自动转换为 subsys_initcall 优先级(启动早期),导致驱动注册太早,I2C 总线还没就绪
后面我只能使用系统自带的goodix,gt9147了!!!
编译:

	make dtbs

复制到开发板中:

	sudo cp arch/arm/boot/dts/stm32mp157d-atk.dtb /home/chensir/linux/tftpboot/ -f

2、编写驱动程序

之前的博客也是跟大家按照肌肉记忆来编写程序!一步一步按照思路来编写!
总代码会放在最后。
为了让大家更能明白,可以先对着总代码,进行对我的写代码流程更加详细得当!

2.1、头文件

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/input/mt.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/interrupt.h>

可以看出头文件用到了input/interrupt,说明这个例程代码用到了input子系统和中断。

2.2、驱动注册和注销

Pasted image 20250918151115.png
注册和注销一体化:这个意思是init和exit不用开发人员写了!
可以看以下举例代码:
Pasted image 20250918152711.png

2.2.1、编写module_i2c_driver驱动结构体

Pasted image 20250918152832.png
其中流程是:
10行代码:在本模块执行;
11行代码:name是驱动的名称,字符串"edt_ft5426"会显示在/sys/bus/i2c/devices/0-0038/name中;会在driver目录下生成edt_ft5426。这个是驱动开发者自己编写的,和下面的匹配无关!
12行代码:设备树中的compatible值会与edt_ft5426_of_match下的compatible相匹配。
如下图edt_ft5426_of_match的代码:
Pasted image 20250918153555.png
其中MODULE_DEVICE_TABLE是声明一下而已!
compatible值一旦匹配成功,就会执行probe和remove。(这些其实之前的驱动程序讲解已经讲过很多遍了)

2.2.2、编写probe和remove函数

Pasted image 20250918153952.png

2.2.3、注册和注销字符驱动设备

我们这里用到的INPUT子系统和I2C框架,所以可以回顾。
input子系统主要是注册和注销字符设备。在/sys/class 目录下有一个 input 子目录,input子系统的所有设备的主设备号是13,不用开发者自己创建了。

1、定义设备结构体

Pasted image 20250918181814.png
struct i2c_client 结构体由内核创建和管理,每个连接到 I2C 总线上的设备(比如你的 FT5426 触摸屏)都会对应一个 i2c_client 实例。它包含了设备的关键信息:

  • 设备地址(如 FT5426 的 0x38);
  • 所属的 I2C 总线(如你系统中的 i2c-0);
  • 设备名称、设备树节点指针等元数据;
  • 用于通信的函数接口(如读取 / 写入设备寄存器)。
2、配置probe和remove函数

这里我们先编译测试一下;
Pasted image 20250918184512.png
这里需要把i2c_set_clientdata(client, ft5426);放在probe函数最后,只有当所有资源初始化完成且验证无误后,才能将 ft5426 与 client 绑定,一:确保绑定的数据是 “可用的完整状态”,二:避免其他函数访问未初始化的资源
Pasted image 20250918184438.png
发现并没有问题!
接下来继续完善probe和remove函数:

2.2.4、注册和注销input子系统
1、定义设备结构体

Pasted image 20250919103156.png
可以看到client和input在设备结构体当中是没有实例的,所以都要进行实例化;

2、配置probe和remove函数

probe函数:
Pasted image 20250919110004.png
Pasted image 20250919104739.png
1- 使用devm_input_allocate_device分配输入设备结构体,这是内核提供的带设备管理的分配函数- 优势:当设备被卸载时,内核会自动释放该内存,无需手动管理。
2- 将分配的输入设备关联到驱动私有数据结构ft5426

  • 设置设备名称,会显示在/proc/bus/input/devices等系统信息中
  • 指明总线类型为 I2C(BUS_I2C),用于系统识别设备连接方式
    3- input_set_abs_params用于配置绝对坐标事件的参数
  • 参数说明:
    • ABS_MT_POSITION_X/Y:多点触摸协议中的 X/Y 坐标事件类型
    • 0, 10240, 600:分别定义 X 轴 (0-1024) 和 Y 轴 (0-600) 的坐标范围
    • 后两个0:分别表示坐标最小变化量和分辨率(此处均设为 0)
      4- input_mt_init_slots初始化多点触摸的 “槽位” 系统
  • 参数说明:
    • MAX_SUPPORT_POINTS:设备支持的最大触摸点数(需在驱动中定义)
    • INPUT_MT_DIRECT:表示这是直接触摸设备(区别于间接触摸如触摸板)
      5-调用input_register_device(input)进行注册输入设备。
      remove函数:
      Pasted image 20250919105329.png
  • 从系统中移除之前通过 input_register_device() 注册的输入设备
  • 通知内核输入子系统该设备已不再可用
2.2.5、复位FT5426触摸芯片
1、定义设备结构体

Pasted image 20250919110409.png

2、配置probe函数

Pasted image 20250919110553.png
Pasted image 20250919110626.png

2.2.6、初始化FT5426
1、配置probe函数

Pasted image 20250919110808.png
这个都是前面I2C协议学过的!
Pasted image 20250919111019.png

2.2.7、申请、注册中断服务函数
1、定义设备结构体

Pasted image 20250919111124.png

2、配置probe函数

Pasted image 20250919111350.png

其中要配置edt_ft5426_ts_isr函数:

Pasted image 20250919111530.png
Pasted image 20250919111548.png

其中需要配置edt_ft5426_ts_read函数:

Pasted image 20250919111829.png

其中涉及的宏定义需要声明:

Pasted image 20250919111952.png

最终完善中断配置:

Pasted image 20250919112112.png
其实不难发现我们这里并没有设涉及字符操作集、cdev、class、device等内容;
解释:
驱动是输入子系统的 “客户端”,子系统已经封装了字符设备的cdev/file_operations/class/device等通用组件,你只需关注 “如何和 FT5426 硬件交互、如何上报触摸事件”,无需重复造轮子。
输入设备(键盘、鼠标、触摸屏等)的核心需求是 “上报事件”(如触摸坐标、按键按下),而非复杂的read/write数据交互。为此,内核输入子系统提前做好了通用封装:

  • 替你实现file_operations:子系统内置了统一的input_fops操作集,包含open/read/poll等函数(用户空间工具如evtest,就是通过这些接口读取触摸事件),你无需自己定义。
  • 替你管理cdev:当你调用input_register_device注册input_dev时,子系统会自动创建cdev结构体,并将其与input_fops绑定,完成字符设备的内核注册。
  • 替你创建classdevice:输入子系统已预定义input_class(对应/sys/class/input/目录),注册input_dev后,子系统会自动在该目录下创建设备节点,并触发 udev 生成/dev/input/eventX(你的触摸屏对应的设备文件),无需手动调用class_createdevice_create

3、实验现象

Pasted image 20251010163109.png

4、总代码

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/input/mt.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/interrupt.h>/* FT5426寄存器相关宏定义 */
#define FT5426_DEVIDE_MODE_REG	0x00	// 模式寄存器
#define FT5426_TD_STATUS_REG    	0x02  	// 状态寄存器
#define FT5426_TOUCH_DATA_REG   	0x03   	// 触摸数据读取的起始寄存器
#define FT5426_ID_G_MODE_REG    	0xA4  	// 中断模式寄存器// 定义触摸屏支持的最大触摸点数,根据你的硬件实际情况修改
#define MAX_SUPPORT_POINTS    5#define TOUCH_EVENT_DOWN      	0x00    // 按下
#define TOUCH_EVENT_UP           	0x01    // 抬起
#define TOUCH_EVENT_ON         	0x02    // 接触
#define TOUCH_EVENT_RESERVED  	0x03    // 保留struct edt_ft5426_dev {struct i2c_client *client;struct input_dev *input;int reset_gpio;int irq_gpio;
};static int edt_ft5426_ts_reset(struct edt_ft5426_dev *ft5426)
{struct i2c_client *client = ft5426->client;int ret;/* 从设备树中获取复位管脚 */ft5426->reset_gpio = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);if (!gpio_is_valid(ft5426->reset_gpio)) {dev_err(&client->dev, "Failed to get ts reset gpio\n");return ft5426->reset_gpio;}/* 申请使用管脚 */ret = devm_gpio_request_one(&client->dev, ft5426->reset_gpio,GPIOF_OUT_INIT_HIGH, "ft5426 reset");if (ret < 0)return ret;msleep(20);gpio_set_value_cansleep(ft5426->reset_gpio, 0);    	// 拉低复位引脚msleep(5);gpio_set_value_cansleep(ft5426->reset_gpio, 1);   	// 拉高复位引脚,结束复位return 0;
}static int edt_ft5426_ts_write(struct edt_ft5426_dev *ft5426,u8 addr, u8 *buf, u16 len)
{struct i2c_client *client = ft5426->client;struct i2c_msg msg;u8 send_buf[6] = {0};int ret;send_buf[0] = addr;memcpy(&send_buf[1], buf, len);msg.flags = 0;                  //i2c写msg.addr = client->addr;msg.buf = send_buf;msg.len = len + 1;ret = i2c_transfer(client->adapter, &msg, 1);if (1 == ret)return 0;else {dev_err(&client->dev, "%s: write error, addr=0x%x len=%d.\n",__func__, addr, len);return -1;}
}static int edt_ft5426_ts_read(struct edt_ft5426_dev *ft5426,u8 addr, u8 *buf, u16 len)
{struct i2c_client *client = ft5426->client;struct i2c_msg msg[2];int ret;msg[0].flags = 0;             	// i2c写msg[0].addr = client->addr;msg[0].buf = &addr;msg[0].len = 1;              	// 1个字节msg[1].flags = I2C_M_RD;    	//i2c读msg[1].addr = client->addr;msg[1].buf = buf;msg[1].len = len;ret = i2c_transfer(client->adapter, msg, 2);if (2 == ret)return 0;else {dev_err(&client->dev, "%s: read error, addr=0x%x len=%d.\n",__func__, addr, len);return -1;}
}static irqreturn_t edt_ft5426_ts_isr(int irq, void *dev_id)
{struct edt_ft5426_dev *ft5426 = dev_id;u8 rdbuf[30] = {0};int i, type, x, y, id;bool down;int ret;/* 读取FT5426触摸点坐标从0x02寄存器开始,连续读取29个寄存器 */ret = edt_ft5426_ts_read(ft5426, FT5426_TD_STATUS_REG, rdbuf, 29);if (ret)goto out;for (i = 0; i < MAX_SUPPORT_POINTS; i++) {u8 *buf = &rdbuf[i * 6 + 1];/* 以第一个触摸点为例,寄存器TOUCH1_XH(地址0x03),各bit位描述如下:* bit7:6  Event flag  0:按下 1:释放 2:接触 3:没有事件* bit5:4  保留* bit3:0  X轴触摸点的11~8位*/type = buf[0] >> 6;                     // 获取触摸点的Event Flagif (type == TOUCH_EVENT_RESERVED)continue;/* 我们所使用的触摸屏和FT5426是反过来的 */x = ((buf[2] << 8) | buf[3]) & 0x0fff;y = ((buf[0] << 8) | buf[1]) & 0x0fff;/* 以第一个触摸点为例,寄存器TOUCH1_YH(地址0x05),各bit位描述如下:* bit7:4  Touch ID  触摸ID,表示是哪个触摸点* bit3:0  Y轴触摸点的11~8位。*/id = (buf[2] >> 4) & 0x0f;down = type != TOUCH_EVENT_UP;input_mt_slot(ft5426->input, id);input_mt_report_slot_state(ft5426->input, MT_TOOL_FINGER, down);if (!down)continue;input_report_abs(ft5426->input, ABS_MT_POSITION_X, x);input_report_abs(ft5426->input, ABS_MT_POSITION_Y, y);}input_mt_report_pointer_emulation(ft5426->input, true);input_sync(ft5426->input);out:return IRQ_HANDLED;
}
static int edt_ft5426_ts_irq(struct edt_ft5426_dev *ft5426)
{struct i2c_client *client = ft5426->client;int ret;/* 从设备树中获取中断管脚 */ft5426->irq_gpio = of_get_named_gpio(client->dev.of_node, "irq-gpios", 0);if (!gpio_is_valid(ft5426->irq_gpio)) {dev_err(&client->dev, "Failed to get ts interrupt gpio\n");return ft5426->irq_gpio;}/* 申请使用管脚 */ret = devm_gpio_request_one(&client->dev, ft5426->irq_gpio,GPIOF_IN, "ft5426 interrupt");if (ret < 0)return ret;/* 注册中断服务函数 */ret = devm_request_threaded_irq(&client->dev, gpio_to_irq(ft5426->irq_gpio),NULL, edt_ft5426_ts_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,client->name, ft5426);if (ret) {dev_err(&client->dev, "Failed to request touchscreen IRQ.\n");return ret;}return 0;
}
static int edt_ft5426_ts_probe(struct i2c_client *client,const struct i2c_device_id *id)
{int ret;struct edt_ft5426_dev *ft5426;struct input_dev *input;u8 data;/* 实例化一个struct edt_ft5426_dev对象 */ft5426 = devm_kzalloc(&client->dev, sizeof(struct edt_ft5426_dev), GFP_KERNEL);if (!ft5426) {dev_err(&client->dev, "Failed to allocate ft5426 driver data.\n");return -ENOMEM;}else{dev_err(&client->dev, "Sucessfully!.\n");}ft5426->client = client;/* 复位FT5426触摸芯片 */ret = edt_ft5426_ts_reset(ft5426);if (ret)return ret;msleep(5);/* 初始化FT5426 */data = 0;edt_ft5426_ts_write(ft5426, FT5426_DEVIDE_MODE_REG, &data, 1);data = 1;edt_ft5426_ts_write(ft5426, FT5426_ID_G_MODE_REG, &data, 1);/* 申请、注册中断服务函数 */ret = edt_ft5426_ts_irq(ft5426);if (ret)return ret;/* 注册input设备 */input = devm_input_allocate_device(&client->dev);if (!input) {dev_err(&client->dev, "Failed to allocate input device.\n");return -ENOMEM;}ft5426->input = input;input->name = "FocalTech FT5426 TouchScreen";input->id.bustype = BUS_I2C;input_set_abs_params(input, ABS_MT_POSITION_X,0, 1024, 0, 0);input_set_abs_params(input, ABS_MT_POSITION_Y,0, 600, 0, 0);ret = input_mt_init_slots(input, MAX_SUPPORT_POINTS, INPUT_MT_DIRECT);if (ret) {dev_err(&client->dev, "Failed to init MT slots.\n");return ret;}ret = input_register_device(input);if (ret)return ret;i2c_set_clientdata(client, ft5426);return 0;
}static int edt_ft5426_ts_remove(struct i2c_client *client)
{struct edt_ft5426_dev *ft5426 = i2c_get_clientdata(client);input_unregister_device(ft5426->input);return 0;
}static const struct of_device_id edt_ft5426_of_match[] = {{ .compatible = "edt,edt-ft5426", },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, edt_ft5426_of_match);static struct i2c_driver edt_ft5426_ts_driver = {.driver = {.owner     		= THIS_MODULE,.name          	= "edt_ft5426",.of_match_table	= of_match_ptr(edt_ft5426_of_match),},.probe    = edt_ft5426_ts_probe,.remove   = edt_ft5426_ts_remove,
};module_i2c_driver(edt_ft5426_ts_driver);MODULE_LICENSE("GPL");
MODULE_AUTHOR("chensir");
MODULE_INFO(intree, "Y");{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, edt_ft5426_of_match);static struct i2c_driver edt_ft5426_ts_driver = {.driver = {.owner     		= THIS_MODULE,.name          	= "edt_ft5426",.of_match_table	= of_match_ptr(edt_ft5426_of_match),},.probe    = edt_ft5426_ts_probe,.remove   = edt_ft5426_ts_remove,
};module_i2c_driver(edt_ft5426_ts_driver);MODULE_LICENSE("GPL");
MODULE_AUTHOR("chensir");
MODULE_INFO(intree, "Y");
http://www.dtcms.com/a/466931.html

相关文章:

  • 了解学习MySQL数据库基础
  • 做网站怎么选服务器服务器网站怎么做
  • 长沙微信网站开发学习网页制作学什么
  • 超越RTL的系统设计:ESL设计的新范式与CIRCT的桥梁作用
  • JVM的即时编译JIT的介绍
  • 网站建设心得8000字网站域名设计推荐
  • 十堰哪里有做网站的搜索引擎营销的案例有哪些
  • 网站空间流量不够服务器建网站
  • 长宁苏州网站建设公司cms系统表单
  • 化妆品营销型网站案例工商管理系统官网
  • 免费的推广网站有哪些wordpress 36kr 模板
  • 网站建设对企业的意义太白县住房和城乡建设局网站
  • 保山市住房和城乡建设厅网站搜索引擎网站建设
  • 【MySQL在Ubuntu系统下的安装方法】保姆级教程
  • 网站服务器怎么查询网站建设服务器怎么设置
  • 南京建设集团网站做好产品策划的重要性
  • ModStartCMS v9.7.0 组件升级优化,模块升级提醒,访问明细导出
  • 网站如何安装wordpress爆款采集推广引流软件
  • 双目测距实战2-相机标定过程
  • anthropics-claude-cookbooks学习记录01
  • 品网站建设河南省住房和城乡建设部网站首页
  • 人工智能重塑未来经济:转型、挑战与出路
  • Pixels(像素)
  • dedecms网站备份企业光纤局域网组网方案
  • 深入解析 MTE 测试中的 Paging 流量与 S1 接口
  • 泰州建设局网站安监站通报低功耗集成主板做网站
  • 安卓AIDL跨应用通讯的实现
  • 如何做一个花店小程序,搭建一个小程序多少钱
  • 电商网站公司木兰网
  • 网站投放广告教程怎么创建官网主页