触摸屏I2C驱动移植

触摸屏简介

GT911 是专为 7”~8” 设计的新一代 5 点电容触控方案,拥有 26 个驱动通道和 14 个感应通道,
以满足更高的 touch 精度要求。
GT911 可同时识别 5 个触摸点位的实时准确位置,移动轨迹及触摸面积。并可根据主控需要,读取相应点数的触摸信息。
触摸屏I2C驱动移植_第1张图片

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:

触摸屏I2C驱动移植_第2张图片

 

配置触摸屏驱动

以友善电子的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子系统(框架) 

 软件框架简图:触摸屏I2C驱动移植_第3张图片

 硬件框架简图:

触摸屏I2C驱动移植_第4张图片

 

查找匹配的设备树节点

kernel/arch/arm64/boot/dts/rockchip$ grep -nir "goodix_ts" rk3399*

rk3399-nanopi4-common.dtsi:851: gt9xx: goodix_ts@5d {

触摸屏I2C驱动移植_第5张图片

 i2c4:i2c编号为4的控制器节点,包含了goodix,gt9xx的从设备信息。

所以,i2c4在驱动加载时,顺便生成了goodix,gt9xx的从设备信息,并放入从设备总线上,等着自己写的驱动程序匹配(通过competible属性);

i2c驱动框架用到5个核心对象

触摸屏I2C驱动移植_第6张图片

 i2c驱动分层原理图:触摸屏I2C驱动移植_第7张图片

 

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
    
              

你可能感兴趣的:(linux驱动开发,单片机,嵌入式硬件)