触摸屏简介
I2C 协议及 i2c 器件连接
每个连接到总线的器件都可使用软件根据它唯一的地址来识别,传输数据的设备之间是主从关系。
主机可做主机发送器或主机接收器;
多主机总线:两个或多个主机可同时发起数据传输,通过冲突检测及仲裁防止数据被破坏。
串行的 8 位双向数据传输。
主机:发起/停止数据传输,提供时钟信号的器件;
从机:被主机寻址的器件;
发送器:发送数据到总线的器件;
接收器:从总线接收数据的器件;
i2c 信号
S:SCL 高电平,SDA 由高到低跳变,开始传送数据;
P:SCL 高电平,SDA 由低到高跳变,结束传送数据;
transmitter 发送器 receiver 接收器;ACK:接收器在接收到 8 位数据后,在第 9 个时钟周期,拉低 SDA 电平;SDA 必须在 SCL 为高电平时有效,sda 数据必须在 SCL 为低电平下才可以发生变化;在第九个时钟是,主控会将 sda 拉高释放控制权,从设备收完数据后将 SDA 拉低,表示ack.
I2c 总线数据传输格式
发送到 SDA 总线上的每个字节必须是 8 位,每次传输可以发送的字节数量不受限制,每个字节后必须有一个响应位.先传输数据的高位(MSB).
GT9系列i2c接口有两个器件地址可用,我们用的是第一个0x5d:
配置触摸屏驱动
以友善电子的rk3399装的安卓系统为例;
打开系统配置菜单:
ron@ubuntu:~/work/rk3399-android-8.1$ cd kernel/
ron@ubuntu:~/work/rk3399-android-8.1/kernel$ make menuconfig
找到触摸屏Goodix GT9xx配置的Multi-touch Protocol A,取消选择、编译、重烧后,此屏幕触摸功能就会失效,原因是我们去掉了Goodix GT9xx的驱动程序。
linux i2c子系统(框架)
硬件框架简图:
查找匹配的设备树节点
kernel/arch/arm64/boot/dts/rockchip$ grep -nir "goodix_ts" rk3399*
rk3399-nanopi4-common.dtsi:851: gt9xx: goodix_ts@5d {
i2c4:i2c编号为4的控制器节点,包含了goodix,gt9xx的从设备信息。
所以,i2c4在驱动加载时,顺便生成了goodix,gt9xx的从设备信息,并放入从设备总线上,等着自己写的驱动程序匹配(通过competible属性);
i2c驱动框架用到5个核心对象
i2c设备完成代码分析:
i2c子系统 工作原理分析
从两条主线入手
1.调用关系 如果贯穿?
应用
ret =read(fd, buf, sizeof(buf));//===========应用层
|
|
sys_read
|
|
gt9xx_read //===========从设备驱动层
|
|
ret = gtp_i2c_read(i2c_connect_client, buf, sizeof(buf));
|
|
struct i2c_msg msgs[2]; //创建2个数据包对象
msgs[0].flags = !I2C_M_RD; //写标志
msgs[0].addr = client->addr; //器件地址
msgs[0].len = GTP_ADDR_LENGTH;//16位地址
msgs[0].buf = &buf[0]; //存放寻址用地址的首地址
msgs[1].flags = I2C_M_RD; //读标志
msgs[1].addr = client->addr; //器件地址
msgs[1].len = len - GTP_ADDR_LENGTH; //设置要读的字节数
msgs[1].buf = &buf[GTP_ADDR_LENGTH];//存放读出来的内容的首地址
ret = i2c_transfer(client->adapter, msgs, 2);//将数据包交给底层函数进行收发数据包
|
| //===================== i2c核心层
ret = __i2c_transfer(adap, msgs, num);
|
|
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
| 函数指针找不到定义
| //======================???
}
2.分层 每层原理2.1 适配器层
入口和出口函数被封装
module_platform_driver(rk3x_i2c_driver);#define module_platform_driver(__platform_driver) \
module_driver(__platform_driver, platform_driver_register, \
platform_driver_unregister)#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);//创建平台驱动对象并初始化
static struct platform_driver rk3x_i2c_driver = {
.probe = rk3x_i2c_probe,
.remove = rk3x_i2c_remove,
.driver = {
.name = "rk3x-i2c",
.of_match_table = rk3x_i2c_match,//匹配条件
.pm = &rk3x_i2c_pm_ops,
},
};static const struct of_device_id rk3x_i2c_match[] = {
。。。
},
{
.compatible = "rockchip,rk3399-i2c",
.data = (void *)&rk3399_soc_data
},
{},
};struct rk3x_i2c {
struct i2c_adapter adap;
struct device *dev;
wait_queue_head_t wait;
。。。
};
//设备树节点和平台驱动匹配成功
static int rk3x_i2c_probe(struct platform_device *pdev)
|
struct device_node *np = pdev->dev.of_node; //拿节点对象
struct rk3x_i2c *i2c;//本地结构体对象
i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL);//创建本地对象
//初始化adapter对象
strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));//i2c-4 名字
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &rk3x_i2c_algorithm; //注册算法对象 i2c通信就可以调下去了
i2c->adap.retries = 3;
i2c->adap.dev.of_node = np;
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
//拿地址资源并IO映射
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(i2c->regs))
return PTR_ERR(i2c->regs);
//拿中断资源申请中断
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "cannot find rk3x IRQ\n");
return irq;
}ret = devm_request_irq(&pdev->dev, irq, rk3x_i2c_irq,
0, dev_name(&pdev->dev), i2c);
//时钟初始化ret = i2c_add_adapter(&i2c->adap);//注册adapter对象
|
|
i2c_register_adapter(adapter);
|
dev_set_name(&adap->dev, "i2c-%d", adap->nr); // /dev/i2d-0,1,2,
adap->dev.bus = &i2c_bus_type; //adapter注册 到 i2c_bus_type
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev);
of_i2c_register_devices(adap);
|
|
for_each_available_child_of_node(adap->dev.of_node, node) {//遍历i2c4下的子节点
if (of_node_test_and_set_flag(node, OF_POPULATED))
continue;
of_i2c_register_device(adap, node);
|
|
struct i2c_board_info info = {};
addr_be = of_get_property(node, "reg", &len); //读i2c4下的子节点的reg属性
addr = be32_to_cpup(addr_be);
info.addr = addr;
result = i2c_new_device(adap, &info);
|
|
struct i2c_client *client;
client = kzalloc(sizeof *client, GFP_KERNEL);//创建client对象
client->adapter = adap;//绑定adapter
client->addr = info->addr;//设置client的器件地址
client->dev.bus = &i2c_bus_type;//client注册到i2c总线
}
2.2从设备驱动层
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
|
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type; //i2c_driver注册 到 i2c_bus_type