Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长
本章的思维导图内容如下:

二、硬件原理图分析

一个复位 IO、一个中断 IO、 I2C2 的 SCL 和 SDA。
检查有没有被占用。
- &i2c2 {
- 2 clock_frequency = <100000>;
- 3 pinctrl-names = "default";
- 4 pinctrl-0 = <&pinctrl_i2c2>;
- 5 status = "okay";
- 6 7
- /****************************/
- 8 /* 省略掉其他的设备节点 */
- 9 /****************************/
- 10
- 11 /* zuozhongkai FT5406/FT5426 */
- 12 ft5426: ft5426@38 {
- 13 compatible = "edt,edt-ft5426";
- 14 reg = <0x38>;
- 15 pinctrl-names = "default";
- 16 pinctrl-0 = <&pinctrl_tsc
- 17 &pinctrl_tsc_reset >;
- 18 interrupt-parent = <&gpio1>;
- 19 interrupts = <9 0>;
- 20 reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
- 21 interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
- 22 };
- 23 };
- 1 #include
- 2 #include
- ......
- 15 #include
- 16 /***************************************************************
- 17 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
- 18 文件名 : ft5x06.c
- 19 作者 : 左忠凯
- 20 版本 : V1.0
- 21 描述 : FT5X06,包括 FT5206、 FT5426 等触摸屏驱动程序
- 22 其他 : 无
- 23 论坛 : www.openedv.com
- 24 日志 : 初版 V1.0 2019/12/23 左忠凯创建个
- 25 ***************************************************************/
- 26
- 27 #define MAX_SUPPORT_POINTS 5 /* 5 点触摸 */
- 28 #define TOUCH_EVENT_DOWN 0x00 /* 按下 */
- 29 #define TOUCH_EVENT_UP 0x01 /* 抬起 */
- 30 #define TOUCH_EVENT_ON 0x02 /* 接触 */
- 31 #define TOUCH_EVENT_RESERVED 0x03 /* 保留 */
- 32
- 33 /* FT5X06 寄存器相关宏定义 */
- 34 #define FT5X06_TD_STATUS_REG 0X02 /* 状态寄存器地址 */
- 35 #define FT5x06_DEVICE_MODE_REG 0X00 /* 模式寄存器 */
- 36 #define FT5426_IDG_MODE_REG 0XA4 /* 中断模式 */
- 37 #define FT5X06_READLEN 29 /* 要读取的寄存器个数 */
- 38
- 39 struct ft5x06_dev {
- 40 struct device_node *nd; /* 设备节点 */
- 41 int irq_pin,reset_pin; /* 中断和复位 IO */
- 42 int irqnum; /* 中断号 */
- 43 void *private_data; /* 私有数据 */
- 44 struct input_dev *input; /* input 结构体 */
- 45 struct i2c_client *client; /* I2C 客户端 */
- 46 };
- 47
- 48 static struct ft5x06_dev ft5x06;
- 49
- 50 /*
- 51 * @description : 复位 FT5X06
- 52 * @param - client : 要操作的 i2c
- 53 * @param – multidev : 自定义的 multitouch 设备
- 54 * @return : 0,成功;其他负值,失败
- 55 */
- 56 static int ft5x06_ts_reset(struct i2c_client *client,
- struct ft5x06_dev *dev)
- 57 {
- 58 int ret = 0;
- 59
- 60 if (gpio_is_valid(dev->reset_pin)) { /* 检查 IO 是否有效 */
- 61 /* 申请复位 IO,并且默认输出低电平 */
- 62 ret = devm_gpio_request_one(&client->dev,
- 63 dev->reset_pin, GPIOF_OUT_INIT_LOW,
- 64 "edt-ft5x06 reset");
- 65 if (ret) {
- 66 return ret;
- 67 }
- 68 msleep(5);
- 69 gpio_set_value(dev->reset_pin, 1); /* 输出高电平,停止复位 */
- 70 msleep(300);
- 71 }
- 72 return 0;
- 73 }
- 74
- 75 /*
- 76 * @description : 从 FT5X06 读取多个寄存器数据
- 77 * @param – dev : ft5x06 设备
- 78 * @param – reg : 要读取的寄存器首地址
- 79 * @param – val : 读取到的数据
- 80 * @param – len : 要读取的数据长度
- 81 * @return : 操作结果
- 82 */
- 83 static int ft5x06_read_regs(struct ft5x06_dev *dev, u8 reg,
- void *val, int len)
- 84 {
- 85 int ret;
- 86 struct i2c_msg msg[2];
- 87 struct i2c_client *client = (struct i2c_client *)dev->client;
- ......
- 101 ret = i2c_transfer(client->adapter, msg, 2);
- 102 if(ret == 2) {
- 103 ret = 0;
- 104 } else {
- 105 ret = -EREMOTEIO;
- 106 }
- 107 return ret;
- 108 }
- 109
- 110 /*
- 111 * @description : 向 ft5x06 多个寄存器写入数据
- 112 * @param – dev : ft5x06 设备
- 113 * @param – reg : 要写入的寄存器首地址
- 114 * @param – val : 要写入的数据缓冲区
- 115 * @param – len : 要写入的数据长度
- 116 * @return : 操作结果
- 117 */
- 118 static s32 ft5x06_write_regs(struct ft5x06_dev *dev, u8 reg,
- u8 *buf, u8 len)
- 119 {
- 120 u8 b[256];
- 121 struct i2c_msg msg;
- 122 struct i2c_client *client = (struct i2c_client *)dev->client;
- 123
- 124 b[0] = reg; /* 寄存器首地址 */
- 125 memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组 b 里面 */
- 126
- 127 msg.addr = client->addr; /* ft5x06 地址 */
- 128 msg.flags = 0; /* 标记为写数据 */
- 129
- 130 msg.buf = b; /* 要写入的数据缓冲区 */
- 131 msg.len = len + 1; /* 要写入的数据长度 */
- 132
- 133 return i2c_transfer(client->adapter, &msg, 1);
- 134 }
- 135
- 136 /*
- 137 * @description : 向 ft5x06 指定寄存器写入指定的值,写一个寄存器
- 138 * @param – dev : ft5x06 设备
- 139 * @param – reg : 要写的寄存器
- 140 * @param – data : 要写入的值
- 141 * @return : 无
- 142 */
- 143 static void ft5x06_write_reg(struct ft5x06_dev *dev, u8 reg,
- u8 data)
- 144 {
- 145 u8 buf = 0;
- 146 buf = data;
- 147 ft5x06_write_regs(dev, reg, &buf, 1);
- 148 }
- 149
- 150 /*
- 151 * @description : FT5X06 中断服务函数
- 152 * @param - irq : 中断号
- 153 * @param - dev_id : 设备结构。
- 154 * @return : 中断执行结果
- 155 */
- 156 static irqreturn_t ft5x06_handler(int irq, void *dev_id)
- 157 {
- 158 struct ft5x06_dev *multidata = dev_id;
- 159
- 160 u8 rdbuf[29];
- 161 int i, type, x, y, id;
- 162 int offset, tplen;
- 163 int ret;
- 164 bool down;
- 165
- 166 offset = 1; /* 偏移 1,也就是 0X02+1=0x03,从 0X03 开始是触摸值 */
- 167 tplen = 6; /* 一个触摸点有 6 个寄存器来保存触摸值 */
- 168
- 169 memset(rdbuf, 0, sizeof(rdbuf)); /* 清除 */
- 170
- 171 /* 读取 FT5X06 触摸点坐标从 0X02 寄存器开始,连续读取 29 个寄存器 */
- 172 ret = ft5x06_read_regs(multidata, FT5X06_TD_STATUS_REG,
- rdbuf, FT5X06_READLEN);
- 173 if (ret) {
- 174 goto fail;
- 175 }
- 176
- 177 /* 上报每一个触摸点坐标 */
- 178 for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
- 179 u8 *buf = &rdbuf[i * tplen + offset];
- 180
- 181 /* 以第一个触摸点为例,寄存器 TOUCH1_XH(地址 0X03),各位描述如下:
- 182 * bit7:6 Event flag 0:按下 1:释放 2:接触 3:没有事件
- 183 * bit5:4 保留
- 184 * bit3:0 X 轴触摸点的 11~8 位。
- 185 */
- 186 type = buf[0] >> 6; /* 获取触摸类型 */
- 187 if (type == TOUCH_EVENT_RESERVED)
- 188 continue;
- 189
- 190 /* 我们所使用的触摸屏和 FT5X06 是反过来的 */
- 191 x = ((buf[2] << 8) | buf[3]) & 0x0fff;
- 192 y = ((buf[0] << 8) | buf[1]) & 0x0fff;
- 193
- 194 /* 以第一个触摸点为例,寄存器 TOUCH1_YH(地址 0X05),各位描述如下:
- 195 * bit7:4 Touch ID 触摸 ID,表示是哪个触摸点
- 196 * bit3:0 Y 轴触摸点的 11~8 位。
- 197 */
- 198 id = (buf[2] >> 4) & 0x0f;
- 199 down = type != TOUCH_EVENT_UP;
- 200
- 201 input_mt_slot(multidata->input, id);
- 202 input_mt_report_slot_state(multidata->input, MT_TOOL_FINGER,
- down);
- 203
- 204 if (!down)
- 205 continue;
- 206
- 207 input_report_abs(multidata->input, ABS_MT_POSITION_X, x);
- 208 input_report_abs(multidata->input, ABS_MT_POSITION_Y, y);
- 209 }
- 210
- 211 input_mt_report_pointer_emulation(multidata->input, true);
- 212 input_sync(multidata->input);
- 213
- 214 fail:
- 215 return IRQ_HANDLED;
- 216
- 217 }
- 218
- 219 /*
- 220 * @description : FT5x06 中断初始化
- 221 * @param - client : 要操作的 i2c
- 222 * @param – multidev : 自定义的 multitouch 设备
- 223 * @return : 0,成功;其他负值,失败
- 224 */
- 225 static int ft5x06_ts_irq(struct i2c_client *client,
- struct ft5x06_dev *dev)
- 226 {
- 227 int ret = 0;
- 228
- 229 /* 1,申请中断 GPIO */
- 230 if (gpio_is_valid(dev->irq_pin)) {
- 231 ret = devm_gpio_request_one(&client->dev, dev->irq_pin,
- 232 GPIOF_IN, "edt-ft5x06 irq");
- 233 if (ret) {
- 234 dev_err(&client->dev,
- 235 "Failed to request GPIO %d, error %d\n",
- 236 dev->irq_pin, ret);
- 237 return ret;
- 238 }
- 239 }
- 240
- 241 /* 2,申请中断,client->irq 就是 IO 中断, */
- 242 ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
- 243 ft5x06_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- 244 client->name, &ft5x06);
- 245 if (ret) {
- 246 dev_err(&client->dev, "Unable to request
- touchscreen IRQ.\n");
- 247 return ret;
- 248 }
- 249
- 250 return 0;
- 251 }
- 252
- 253 /*
- 254 * @description : i2c 驱动的 probe 函数,当驱动与
- 255 * 设备匹配以后此函数就会执行
- 256 * @param - client : i2c 设备
- 257 * @param - id : i2c 设备 ID
- 258 * @return : 0,成功;其他负值,失败
- 259 */
- 260 static int ft5x06_ts_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- 261 {
- 262 int ret = 0;
- 263
- 264 ft5x06.client = client;
- 265
- 266 /* 1,获取设备树中的中断和复位引脚 */
- 267 ft5x06.irq_pin = of_get_named_gpio(client->dev.of_node,
- "interrupt-gpios", 0);
- 268 ft5x06.reset_pin = of_get_named_gpio(client->dev.of_node,
- "reset-gpios", 0);
- 269
- 270 /* 2,复位 FT5x06 */
- 271 ret = ft5x06_ts_reset(client, &ft5x06);
- 272 if(ret < 0) {
- 273 goto fail;
- 274 }
- 275
- 276 /* 3,初始化中断 */
- 277 ret = ft5x06_ts_irq(client, &ft5x06);
- 278 if(ret < 0) {
- 279 goto fail;
- 280 }
- 281
- 282 /* 4,初始化 FT5X06 */
- 283 ft5x06_write_reg(&ft5x06, FT5x06_DEVICE_MODE_REG, 0);
- 284 ft5x06_write_reg(&ft5x06, FT5426_IDG_MODE_REG, 1);
- 285
- 286 /* 5, input 设备注册 */
- 287 ft5x06.input = devm_input_allocate_device(&client->dev);
- 288 if (!ft5x06.input) {
- 289 ret = -ENOMEM;
- 290 goto fail;
- 291 }
- 292 ft5x06.input->name = client->name;
- 293 ft5x06.input->id.bustype = BUS_I2C;
- 294 ft5x06.input->dev.parent = &client->dev;
- 295
- 296 __set_bit(EV_KEY, ft5x06.input->evbit);
- 297 __set_bit(EV_ABS, ft5x06.input->evbit);
- 298 __set_bit(BTN_TOUCH, ft5x06.input->keybit);
- 299
- 300 input_set_abs_params(ft5x06.input, ABS_X, 0, 1024, 0, 0);
- 301 input_set_abs_params(ft5x06.input, ABS_Y, 0, 600, 0, 0);
- 302 input_set_abs_params(ft5x06.input, ABS_MT_POSITION_X,
- 0, 1024, 0, 0);
- 303 input_set_abs_params(ft5x06.input, ABS_MT_POSITION_Y,
- 0, 600, 0, 0);
- 304 ret = input_mt_init_slots(ft5x06.input, MAX_SUPPORT_POINTS, 0);
- 305 if (ret) {
- 306 goto fail;
- 307 }
- 308
- 309 ret = input_register_device(ft5x06.input);
- 310 if (ret)
- 311 goto fail;
- 312
- 313 return 0;
- 314
- 315 fail:
- 316 return ret;
- 317 }
- 318
- 319 /*
- 320 * @description : i2c 驱动的 remove 函数,移除 i2c 驱动的时候此函数会执行
- 321 * @param – client : i2c 设备
- 322 * @return : 0,成功;其他负值,失败
- 323 */
- 324 static int ft5x06_ts_remove(struct i2c_client *client)
- 325 {
- 326 /* 释放 input_dev */
- 327 input_unregister_device(ft5x06.input);
- 328 return 0;
- 329 }
- 330
- 331
- 332 /*
- 333 * 传统驱动匹配表
- 334 */
- 335 static const struct i2c_device_id ft5x06_ts_id[] = {
- 336 { "edt-ft5206", 0, },
- 337 { "edt-ft5426", 0, },
- 338 { /* sentinel */ }
- 339 };
- 340
- 341 /*
- 342 * 设备树匹配表
- 343 */
- 344 static const struct of_device_id ft5x06_of_match[] = {
- 345 { .compatible = "edt,edt-ft5206", },
- 346 { .compatible = "edt,edt-ft5426", },
- 347 { /* sentinel */ }
- 348 };
- 349
- 350 /* i2c 驱动结构体 */
- 351 static struct i2c_driver ft5x06_ts_driver = {
- 352 .driver = {
- 353 .owner = THIS_MODULE,
- 354 .name = "edt_ft5x06",
- 355 .of_match_table = of_match_ptr(ft5x06_of_match),
- 356 },
- 357 .id_table = ft5x06_ts_id,
- 358 .probe = ft5x06_ts_probe,
- 359 .remove = ft5x06_ts_remove,
- 360 };
- 361
- 362 /*
- 363 * @description : 驱动入口函数
- 364 * @param : 无
- 365 * @return : 无
- 366 */
- 367 static int __init ft5x06_init(void)
- 368 {
- 369 int ret = 0;
- 370
- 371 ret = i2c_add_driver(&ft5x06_ts_driver);
- 372
- 373 return ret;
- 374 }
- 375
- 376 /*
- 377 * @description : 驱动出口函数
- 378 * @param : 无
- 379 * @return : 无
- 380 */
- 381 static void __exit ft5x06_exit(void)
- 382 {
- 383 i2c_del_driver(&ft5x06_ts_driver);
- 384 }
- 385
- 386 module_init(ft5x06_init);
- 387 module_exit(ft5x06_exit);
- 388 MODULE_LICENSE("GPL");
- 389 MODULE_AUTHOR("zuozhongkai");
代码挺多的,就不贴了。
- 1 KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imxrel_imx_4.1.15_2.1.0_ga_alientek
- ......
- 4 obj-m := ft5x06.o
- ......
- 11 clean:
- 12 $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
- depmod //第一次加载驱动的时候需要运行此命令
- modprobe ft5x06.ko //加载驱动模块
生成eventx触摸文件,触摸屏幕,会看见文件上报信息内容变化。
最后一行,添加一行:
obj-y += ft5x06.o
修改完成以后重新编译 linux 内核,然后用新的 zImage 启动开发板。
hexdump /dev/input/event1 //查看触摸屏原始数据上报信息
查看上报数据内容,检验触屏驱动正确性。
以下内容将在下节更新:
五、tslib移植与使用
1、tslib移植
1)、获取tslib源码
2)、修改tslib源码所属用户
3)、ubuntu工具安装
4)、编译tslib
5)、配置tslib
6)、tslib测试
六、使用内核自带的驱动
1、修改edt-ft5x06.c
2、使能内核自带的FT5X06驱动
3、修改设备树
七、4.3.寸屏触摸驱动实验
1、修改设备树pinctrl_tsc节点内容
2、在设备树的i2c2节点下添加tgt9147子节点
3、修改设备树的lcdif节点
4、编译GT9147驱动文件
八、总结
本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。