待完善
简介:本篇文章只讲涉及到百问网 STM32MP157触摸屏的驱动(电容屏)
基本框图如下:
[触摸屏被按下] --> [中断线触发中断] -->[通过I2C子系统去获取触摸屏数据] --> [上报数据] -->[APP:如果app申请了读取数据的话]
所以完成一个触摸屏驱动需要完成以下事情:
1.input驱动框架
2.i通过I2C控制器完成与触摸屏的通信
3.其他事情
I2C控制器驱动触摸IC:
I2C与从通信流程如下:
起始信号--[地址]--回应 --[数据] --ASK --[数据]--……--[NASK]
在裸机使用I2C(硬件):
流程:初始化-->发送数据(1.操作寄存器,然后判断状态寄存器,或者使用封装好的函数)/接受数据 -->结束
在linux下,这样一套流程变为这样。
APP发送或者接受数据给I2C Device 都是依靠驱动提供的函数。上面裸机的操作==驱动要做的。
1.I2C控制器
介绍I2C控制器,优先介绍几个角色(结构体),这几个角色构成了I2C控制器
I2C控制器驱动 (I2C Bus/Adapter Driver):直接与SoC(比如STM32MP157)内部的I2C硬件控制器打交道。它知道如何配置这个控制器的寄存器来产生起始/停止信号、发送/接收字节、处理ACK/NACK
I2C核心 (I2C Core):它为上层的所有设备驱动提供了一套统一的、标准的API来访问I2C总线,比如i2c_transfer, i2c_smbus_read_byte_data等
I2C客户端驱动 (I2C Client Driver):这是针对挂载在I2C总线上的具体设备(比如触摸屏芯片GT911)的驱动
了解了职责,如何编写一个I2C客户端驱动,并封装好传输函数呢:
1.在设备树中写好硬件信息
2.完成驱动函数。
1.probe函数,获取硬件信息,设置好I2C Client结构体,[设置从机的一些寄存器啥的,一步到位]
2.platform_driver,比如of_match_table:支持什么设备,moudule_init函数 moudule_exit
这样就完成了,当驱动加载好,从总线中匹配到设备信息,调用probe函数,获取了相关信息,I2C控制器地址,设置了速率,停止位等等,可以发送从机地址,发送指令给触控IC寄存器进行一些初始化,也相当于完成
2.触摸IC寄存器介绍:(好像一般也不用我们去写,有初始化指令数组或者固件,不过还是认识以下)
获取产品ID和固件:0x8140
0x8047
这是GT911系列配置信息的起始地址。驱动会将从固件文件加载的配置数据写入到这个地址开始的一大片区域,或者自己写,看数据给不给那个配置数组
0x814E:读取坐标数据
0x8040
I2C控制器:
结构体:
struct i2c_client{
client->adapter;//用来传输需要关注
client->addr //从机地址
client->irq//中断号
client->dev//内核打印调试
};
struct i2c_driver
struct of_device_id
struct file_operations
传输函数:
写
i2c_smbus_write_byte_data();
读
temp = i2c_smbus_read_word_data(data->client, SENSOR_REG_TEMP);
写。总的就三步,[根据配置设置I2C控制器]--[完成配置IC--[写或者读取数据]
扩展下来:
从哪来获取硬件信息,配置信息?设备树
获取配置IC信息?也就是复位,开始读取
如何读取
初始化触摸屏:[获取设备信息]-->[获取硬件资源:GPIO,I2C控制器等等]-->[申请一个I2C client设备] -->[初始化I2C控制器] -->[复位]-->[测试] -->[根据id写配置文件/默认初始化]-->[读写]
分析程序:
//1.申请一个结构体保存设备信息等等
struct goodix_ts_data *ts
ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); //申请空间,Input_device
ts->client = client; i2c_set_clientdata(client, ts); // 关联私有数据
//从设备树中获取信息
ts->gpiod_int = devm_gpiod_get_optional(&ts->client->dev, "irq", GPIOD_IN);
if (IS_ERR(ts->gpiod_int)) return PTR_ERR(ts->gpiod_int);
ts->gpiod_rst = devm_gpiod_get_optional(&ts->client->dev, "reset", GPIOD_IN);
if (IS_ERR(ts->gpiod_rst)) return PTR_ERR(ts->gpiod_rst);
//硬件复位与上电
gpiod_direction_output(ts->gpiod_rst, 0);// 拉低复位 msleep(20);
gpiod_direction_output(ts->gpiod_int, 0); // 拉低INT (选择0xBA/BB地址)
usleep_range(100, 2000); gpiod_direction_output(ts->gpiod_rst, 1); // 释放复位 usleep_range(6000, 10000);
gpiod_direction_input(ts->gpiod_int); // 将INT设回输入模式
//获取ID信息
error = goodix_i2c_test(client); // 尝试读取任意寄存器
if (error) return error;
error = goodix_read_version(ts); // 读取产品ID和版本号
if (error) return error;
设置芯片配置
if (cfg) { goodix_send_cfg(ts, cfg); } // 调用函数完成Input设备注册和中断申请
goodix_configure_dev(ts);
设备树案例(以I2C控制器 3为案例)
&i2c3 {
}
Input子系统:(参考文章Linux 内核设备驱动——Input设备子系统 - 知乎)
如图
Input 子系统由以下组件组成:
- 输入硬件(Input HW):表示正在处理事件的底层设备。这可以是一个物理设备,如鼠标、键盘或触摸屏幕,也可以是虚拟设备,如软件仿真器等。
- 输入驱动程序(Input Driver):负责将底层输入设备的数据转换为通用的 Linux 输入事件类型,并将其发送到内核输入核心(Input Core)中。每个输入驱动程序通常对应于一个特定的硬件设备。
- 输入核心(Input Core):负责管理输入驱动程序和输入设备之间的通信,接收并维护添加/删除输入设备的请求,以及将输入事件发送到适当的应用程序。它还提供了一套 API,使用户空间应用程序可以访问输入子系统服务。
- 事件子系统(Event Subsystem):表示传递给用户空间的输入事件。这是一个回调驱动系统,它将在触发的输入事件后执行特定的用户定义函数,然后返回该事件的状态报告。
- 字符驱动程序(Character Driver):允许在文件系统中呈现输入子系统的属性。用户从文件读取输入事件或向文件写入控制命令。用户可以使用自己熟悉的 List I/O、Poll/Select 等等方法请求状态或数据。
nput_dev:是硬件驱动层,代表一个input设备。 input_handler:是事件处理层,代表一个事件处理器。 input_handle:属于核心层,代表一个配对的input设备与input事件处理器。 input_dev 通过全局的input_dev_list:链接在一起,设备注册的时候完成这个操作。
平常我们写设备驱动,重点需要关心Input_dev,即输入设备。
触摸驱动框架图:
重要结构体:
输入设备
处理程序
nput_handle保存2者之间的联系
设备获取、上报数据
流程:
[注册input_dev] -- [注册input_handle]--[判断匹配 input device 和 idtable进行匹配]--[调用input_handler.connect]
上报数据流程:
goodix_process_events{
1.读取点数和数据
2.上报数据
}
对于第一个需要结合寄存器来看:
这里有[0]是保留的,然后是 [1]是X [3]是Y,[5]是size,操作函数会进行高位低位移位相加组合一个坐标,同时814E,有buffer status ,number of touch points这样可以获取到触摸点数
上报本身就三步:
选择要填写的“栏位” (Select Slot)
填写“栏位”里的具体信息 (Report Data)
提交整张表格 (Synchronize Report)
刷新一下
注册一个INPUT设备:流程
-
分配 (Allocate): 申请一个 input_dev 结构体的内存。
-
配置 (Configure): “填写快递单”,设置该设备的能力和属性。
-
注册 (Register): “提交快递单”,将配置好的设备正式通知给内核。
设备树节点写法:
写一个最简单的触摸屏驱动,qemu(以后补充)
分析触摸屏驱动:自己去内核看
Linux-5.4/drivers/input/touchscreen/goodix.c
一些适配触摸IC的方法(来自百问网教程)
分为如下几步:
1.确认触摸IC和设备地址(没数据手册)
安装i2c tools 使用指令
--代表没有该地址的I2C设备,UU代表有驱动在使用,其他如38和60需要拔掉屏幕再测试,然后去内核找芯片型号,
grep "@38" * -nR。
2.在设备树中指定触摸IC信息
1.接在哪个I2C控制器上
2.I2C地址
3.复位引脚
4.中断引脚 ,有效电平
搜索以下touchscreen 为@38
修改为自己I2C控制器引脚,INT和reset引脚
然后去找驱动程序,根据compatible去找
再去内核配置。更新系统
调试:找出问题:省略思路,仅提供一个问题的解决方法