• Linux学习第44天:Linux 多点电容触摸屏实验(二):难忘记第一次牵你手的温存


    Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长 


            本章的思维导图内容如下:

    二、硬件原理图分析

    三、实验程序编写

    1、修改设备树

    1)、添加FT5426所使用的IO

            一个复位 IO、一个中断 IO、 I2C2 的 SCL 和 SDA。

            检查有没有被占用。

    2)、添加FT5426节点

    1. &i2c2 {
    2. 2 clock_frequency = <100000>;
    3. 3 pinctrl-names = "default";
    4. 4 pinctrl-0 = <&pinctrl_i2c2>;
    5. 5 status = "okay";
    6. 6 7
    7. /****************************/
    8. 8 /* 省略掉其他的设备节点 */
    9. 9 /****************************/
    10. 10
    11. 11 /* zuozhongkai FT5406/FT5426 */
    12. 12 ft5426: ft5426@38 {
    13. 13 compatible = "edt,edt-ft5426";
    14. 14 reg = <0x38>;
    15. 15 pinctrl-names = "default";
    16. 16 pinctrl-0 = <&pinctrl_tsc
    17. 17 &pinctrl_tsc_reset >;
    18. 18 interrupt-parent = <&gpio1>;
    19. 19 interrupts = <9 0>;
    20. 20 reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
    21. 21 interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
    22. 22 };
    23. 23 };

    2、编写多点电容触摸驱动

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

            代码挺多的,就不贴了。

    四、运行测试

    1、编译驱动程序

    1. 1 KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imxrel_imx_4.1.15_2.1.0_ga_alientek
    2. ......
    3. 4 obj-m := ft5x06.o
    4. ......
    5. 11 clean:
    6. 12 $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

    2、运行测试

    1. depmod //第一次加载驱动的时候需要运行此命令
    2. modprobe ft5x06.ko //加载驱动模块

            生成eventx触摸文件,触摸屏幕,会看见文件上报信息内容变化。

    3、将驱动添加到内核中

    1)、将驱动文件放到合适的位置

    2)、修改对应的Makefile

            最后一行,添加一行:

    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驱动文件

    八、总结


    本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

  • 相关阅读:
    二维空间下的向量旋转
    STC89C51基础及项目第14天:循迹小车、跟随小车
    python函数式编程?
    腾讯云服务器手动建立WordPress个人站点Windows系统教程-Unirech腾讯云国际版代充
    缩短汽车服务企业供销链,数商云经销商平台渠道订货模块打造企业销售新模式
    【大数据入门核心技术-Hive】Hive3.1.2高可用集群搭建
    MySQL 高级(进阶) SQL 语句(二) -----存储过程
    揭开MyBatis的神秘面纱:掌握动态代理在底层实现中的精髓
    C#访问修饰符
    【Hadoop】MapReduce 编程案例-WordCount
  • 原文地址:https://blog.csdn.net/jiage987450/article/details/134517179