美文网首页
迅为IMX6ULL开发板Linux下电容触摸屏实验-实验程序编写

迅为IMX6ULL开发板Linux下电容触摸屏实验-实验程序编写

作者: TL_6cdd | 来源:发表于2020-11-30 10:20 被阅读0次

54.2  硬件原理图

在本实验中使用迅为的 7 寸屏为例,使用的是 FT5426 触摸芯片。

从原理图中得知,7 寸屏使用 I2C2,触摸屏复位引脚为 SNVS_TAMPER9,中断引脚为 GPIO_9。

54.3  实验程序编写

54.3.1 修改设备树文件

1 、添加 FT5426 的 的 pinctrl  信息

FT5426 触摸芯片用到了 4 个 IO,一个复位 IO、一个中断 IO、I2C2 的 SCL 和 SDA,所以我们需要先在设备树中添加 IO 相关的信息。复位 IO 和中断 IO 是普通的 GPIO,因此这两个 IO 可以放到同一个节点下去描述,I2C2 的 SCL 和 SDA 属于 I2C2,因此这两个要放到同一个节点下去描述。首先是复位 IO 和中断 IO,topeet_emmc_4_3.dts 文件里面默认有个名为“pinctrl_tsc”的节点,如果被删除了的话就自行创建,在此节点下添加触摸屏的复位 IO 和中断 IO 信息,修改以后的“pinctrl_tsc”节点内容如下所示:

1 pinctrl_tsc: tscgrp {

2 fsl,pins = <

3 MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x10B0 /* TSC_RST */

4 MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0xF080 /* TSC_INT */

5 >;

6 };

继续添加 I2C2 的 SCL 和 SDA 这两个 IO 信息,topeet_emmc_4_3.dts 里面默认就已经添加了 I2C2的 IO 信息,这是 NXP 官方添加的,所以不需要我们去修改。找到“pinctrl_i2c1”节点,此节点就是用于描述 I2C2 的 IO 信息,节点内容如下所示:

1 pinctrl_i2c2: i2c2grp {

2 fsl,pins = <

3 MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0

4 MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0

5 >;

6 };

最后在检查一下这四个引脚有没有被其他外设使用。如果有的话就需要屏蔽掉。

2 、添加 FT5426  节点

FT5426 这个触摸 IC 挂载 I2C2 下,因此需要向 I2C2 节点下添加一个子节点,此子节点用于描述FT5426,添加完成以后的 I2C2 节点内容如下所示:

1 &i2c2 {

2 clock_frequency = <100000>;

3 pinctrl-names = "default";

4 pinctrl-0 = <&pinctrl_i2c2>;

5 status = "okay";

6

7 /****************************/

8 /* 省略掉其他的设备节点 */

9 /****************************/

10

11

12 ft5426: ft5426@38 {

13 compatible = "edt,edt-ft5426";

14 reg = <0x38>;

15 pinctrl-names = "default";

16 pinctrl-0 = <&pinctrl_tsc>;

17 interrupt-parent = <&gpio1>;

18 interrupts = <9 0>;

19 reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;

20 interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;

21 };

22 };

第 12 行,触摸屏所使用的 FT5426 芯片节点,挂载 I2C2 节点下,FT5426 的器件地址为 0X38。

第 14 行,reg 属性描述 FT5426 的器件地址为 0x38。

第 16 行,pinctrl-0 属性描述 FT5426 的复位 IO 和中断 IO 所使用的节点为 pinctrl_tsc。

第 17 行,interrupt-parent 属性描述中断 IO 对应的 GPIO 组为 GPIO1。

第 18 行,interrupts 属性描述中断 IO 对应的是 GPIO1 组的 IOI09。

第 19 行,reset-gpios 属性描述复位 IO 对应的 GPIO 为 GPIO5_IO09。

第 20 行,interrupt-gpios 属性描述中断 IO 对应的 GPIO 为 GPIO1_IO09。

54.3.2  编写多点电容触摸驱动

本实验例程路径:i.MX6UL 终结者光盘资料/06_Linux 驱动例程/20_multitouch

创建 ft5426.c 驱动文件,内容如下:

1 #include

2 #include

3 #include

4 #include

5 #include

6 #include

7 #include

8 #include

9 #include

10 #include

11 #include

12 #include

13 #include

14 #include

15 #include

16

17 #define MAX_SUPPORT_POINTS 5 /* 5 点触摸 */

18 #define TOUCH_EVENT_DOWN 0x00 /* 按下 */

19 #define TOUCH_EVENT_UP 0x01 /* 抬起 */

20 #define TOUCH_EVENT_ON 0x02 /* 接触 */

21 #define TOUCH_EVENT_RESERVED 0x03 /* 保留 */

22

23 /* FT5X06 寄存器相关宏定义 */

24 #define FT5X06_TD_STATUS_REG 0X02 /* 状态寄存器地址 */

25 #define FT5x06_DEVICE_MODE_REG 0X00 /* 模式寄存器 */

26 #define FT5426_IDG_MODE_R 0XA4 /* 中断模式 */

27 #define FT5X06_READLEN 29 /* 要读取的寄存器个数 */

28

29 struct ft5x06_dev {

30 struct device_node *nd; /* 设备节点 */

31 int irq_pin,reset_pin; /* 中断和复位 IO */

32 int irqnum; /* 中断号 */

33 void *private_data; /* 私有数据 */

34 struct input_dev *input; /* input 结构体 */

35 struct i2c_client *client; /* I2C 客户端 */

36 };

37

38 static struct ft5x06_dev ft5x06;

39

40 /*

41 * @description : 复位 FT5X06

42 * @param - client : 要操作的 i2c

43 * @param - multidev: 自定义的 multitouch 设备

44 * @return : 0,成功;其他负值,失败

45 */

46 static int ft5x06_ts_reset(struct i2c_client *client, struct ft5x06_dev *dev)

47 {

48 int ret = 0;

49

50 if (gpio_is_valid(dev->reset_pin)) { /* 检查 IO 是否有效 */

51 /* 申请复位 IO,并且默认输出低电平 */

52 ret = devm_gpio_request_one(&client->dev,

53 dev->reset_pin, GPIOF_OUT_INIT_LOW,

54 "edt-ft5x06 reset");

55 if (ret) {

56 return ret;

57 }

58

59 msleep(5);

60 gpio_set_value(dev->reset_pin, 1); /* 输出高电平,停止复位 */

61 msleep(300);

62 }

63

64 return 0;

65 }

66

67 /*

68 * @description : 从 FT5X06 读取多个寄存器数据

69 * @param - dev: ft5x06 设备

70 * @param - reg: 要读取的寄存器首地址

71 * @param - val: 读取到的数据

72 * @param - len: 要读取的数据长度

73 * @return : 操作结果

74 */

75 static int ft5x06_read_regs(struct ft5x06_dev *dev, u8 reg, void *val, int len)

76 {

77 int ret;

78 struct i2c_msg msg[2];

79 struct i2c_client *client = (struct i2c_client *)dev->client;

80

81 /* msg[0]为发送要读取的首地址 */

82 msg[0].addr = client->addr; /* ft5x06 地址 */

83 msg[0].flags = 0; /* 标记为发送数据 */

84 msg[0].buf = ® /* 读取的首地址 */

85 msg[0].len = 1; /* reg 长度*/

86

87 /* msg[1]读取数据 */

88 msg[1].addr = client->addr; /* ft5x06 地址 */

89 msg[1].flags = I2C_M_RD; /* 标记为读取数据*/

90 msg[1].buf = val; /* 读取数据缓冲区 */

91 msg[1].len = len; /* 要读取的数据长度*/

92

93 ret = i2c_transfer(client->adapter, msg, 2);

94 if(ret == 2) {

95 ret = 0;

96 } else {

97 ret = -EREMOTEIO;

98 }

99 return ret;

100 }

101

102 /*

103 * @description : 向 ft5x06 多个寄存器写入数据

104 * @param - dev: ft5x06 设备

105 * @param - reg: 要写入的寄存器首地址

106 * @param - val: 要写入的数据缓冲区

107 * @param - len: 要写入的数据长度

108 * @return : 操作结果

109 */

110 static s32 ft5x06_write_regs(struct ft5x06_dev *dev, u8 reg, u8 *buf, u8 len)

111 {

112 u8 b[256];

113 struct i2c_msg msg;

114 struct i2c_client *client = (struct i2c_client *)dev->client;

115

116 b[0] = reg; /* 寄存器首地址 */

117 memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组 b 里面 */

118

119 msg.addr = client->addr; /* ft5x06 地址 */

120 msg.flags = 0; /* 标记为写数据 */

121

122 msg.buf = b; /* 要写入的数据缓冲区 */

123 msg.len = len + 1; /* 要写入的数据长度 */

124

125 return i2c_transfer(client->adapter, &msg, 1);

126 }

127

128 /*

129 * @description : 向 ft5x06 指定寄存器写入指定的值,写一个寄存器

130 * @param - dev: ft5x06 设备

131 * @param - reg: 要写的寄存器

132 * @param - data: 要写入的值

133 * @return : 无

134 */

135 static void ft5x06_write_reg(struct ft5x06_dev *dev, u8 reg, u8 data)

136 {

137 u8 buf = 0;

138 buf = data;

139 ft5x06_write_regs(dev, reg, &buf, 1);

140 }

141

142 /*

143 * @description : FT5X06 中断服务函数

144 * @param - irq : 中断号

145 * @param - dev_id : 设备结构。

146 * @return : 中断执行结果

147 */

148 static irqreturn_t ft5x06_handler(int irq, void *dev_id)

149 {

150 struct ft5x06_dev *multidata = dev_id;

151

152 u8 rdbuf[29];

153 int i, type, x, y, id;

154 int offset, tplen;

155 int ret;

156 bool down;

157

158 offset = 1; /* 偏移 1,也就是 0X02+1=0x03,从 0X03 开始是触摸值 */

159 tplen = 6; /* 一个触摸点有 6 个寄存器来保存触摸值 */

160

161 memset(rdbuf, 0, sizeof(rdbuf)); /* 清除 */

162

163 /* 读取 FT5X06 触摸点坐标从 0X02 寄存器开始,连续读取 29 个寄存器 */

164 ret = ft5x06_read_regs(multidata, FT5X06_TD_STATUS_REG,

rdbuf, FT5X06_READLEN);

165 if (ret) {

166 goto fail;

167 }

168

169 /* 上报每一个触摸点坐标 */

170 for (i = 0; i < MAX_SUPPORT_POINTS; i++) {

171 u8 *buf = &rdbuf[i * tplen + offset];

172

173 /* 以第一个触摸点为例,寄存器 TOUCH1_XH(地址 0X03),各位描述如下:

174 * bit7:6 Event flag 0:按下 1:释放 2:接触 3:没有事件

175 * bit5:4 保留

176 * bit3:0 X 轴触摸点的 11~8 位。

177 */

178 type = buf[0] >> 6; /* 获取触摸类型 */

179 if (type == TOUCH_EVENT_RESERVED)

180 continue;

181

182 /* 我们所使用的触摸屏和 FT5X06 是反过来的 */

183 x = ((buf[2] << 8) | buf[3]) & 0x0fff;

184 y = ((buf[0] << 8) | buf[1]) & 0x0fff;

185

186 /* 以第一个触摸点为例,寄存器 TOUCH1_YH(地址 0X05),各位描述如下:

187 * bit7:4 Touch ID 触摸 ID,表示是哪个触摸点

188 * bit3:0 Y 轴触摸点的 11~8 位。

189 */

190 id = (buf[2] >> 4) & 0x0f;

191 down = type != TOUCH_EVENT_UP;

192

193 input_mt_slot(multidata->input, id);

194 input_mt_report_slot_state(multidata->input, MT_TOOL_FINGER, down);

195

196 if (!down)

197 continue;

198

199 input_report_abs(multidata->input, ABS_MT_POSITION_X, x);

200 input_report_abs(multidata->input, ABS_MT_POSITION_Y, y);

201 }

202

203 input_mt_report_pointer_emulation(multidata->input, true);

204 input_sync(multidata->input);

205

206 fail:

207 return IRQ_HANDLED;

208

209 }

210

211 /*

212 * @description : FT5x06 中断初始化

213 * @param - client : 要操作的 i2c

214 * @param - multidev: 自定义的 multitouch 设备

215 * @return : 0,成功;其他负值,失败

216 */

217 static int ft5x06_ts_irq(struct i2c_client *client, struct ft5x06_dev *dev)

218 {

219 int ret = 0;

220

221 /* 1,申请中断 GPIO */

222 if (gpio_is_valid(dev->irq_pin)) {

223 ret = devm_gpio_request_one(&client->dev, dev->irq_pin,

224 GPIOF_IN, "edt-ft5x06 irq");

225 if (ret) {

226 dev_err(&client->dev,

227 "Failed to request GPIO %d, error %d\n",

228 dev->irq_pin, ret);

229 return ret;

230 }

231 }

232

233 /* 2,申请中断,client->irq 就是 IO 中断, */

234 ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,

235 ft5x06_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,

236 client->name, &ft5x06);

237 if (ret) {

238 dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");

239 return ret;

240 }

241

242 return 0;

243 }

244

245 /*

246 * @description : i2c 驱动的 probe 函数,当驱动与

247 * 设备匹配以后此函数就会执行

248 * @param - client : i2c 设备

249 * @param - id : i2c 设备 ID

250 * @return : 0,成功;其他负值,失败

251 */

252 static int ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)

253 {

254 int ret = 0;

255

256 ft5x06.client = client;

257

258 /* 1,获取设备树中的中断和复位引脚 */

259 ft5x06.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);

260 ft5x06.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);

261

262 /* 2,复位 FT5x06 */

263 ret = ft5x06_ts_reset(client, &ft5x06);

264 if(ret < 0) {

265 goto fail;

266 }

267

268 /* 3,初始化中断 */

269 ret = ft5x06_ts_irq(client, &ft5x06);

270 if(ret < 0) {

271 goto fail;

272 }

273

274 /* 4,初始化 FT5X06 */

275 ft5x06_write_reg(&ft5x06, FT5x06_DEVICE_MODE_REG, 0); /* 进入正常模式*/

276 ft5x06_write_reg(&ft5x06, FT5426_IDG_MODE_REG, 1); /* FT5426 中断模式 */

277

278 /* 5,input 设备注册 */

279 ft5x06.input = devm_input_allocate_device(&client->dev);

280 if (!ft5x06.input) {

281 ret = -ENOMEM;

282 goto fail;

283 }

284 ft5x06.input->name = client->name;

285 ft5x06.input->id.bustype = BUS_I2C;

286 ft5x06.input->dev.parent = &client->dev;

287

288 __set_bit(EV_KEY, ft5x06.input->evbit);

289 __set_bit(EV_ABS, ft5x06.input->evbit);

290 __set_bit(BTN_TOUCH, ft5x06.input->keybit);

291

292 input_set_abs_params(ft5x06.input, ABS_X, 0, 1024, 0, 0);

293 input_set_abs_params(ft5x06.input, ABS_Y, 0, 600, 0, 0);

294 input_set_abs_params(ft5x06.input, ABS_MT_POSITION_X,0, 1024, 0, 0);

295 input_set_abs_params(ft5x06.input, ABS_MT_POSITION_Y,0, 600, 0, 0);

296 ret = input_mt_init_slots(ft5x06.input, MAX_SUPPORT_POINTS, 0);

297 if (ret) {

298 goto fail;

299 }

300

301 ret = input_register_device(ft5x06.input);

302 if (ret)

303 goto fail;

304

305 return 0;

306

307 fail:

308 return ret;

309 }

310

311 /*

312 * @description : i2c 驱动的 remove 函数,移除 i2c 驱动的时候此函数会执行

313 * @param - client : i2c 设备

314 * @return : 0,成功;其他负值,失败

315 */

316 static int ft5x06_ts_remove(struct i2c_client *client)

317 {

318 /* 释放 input_dev */

319 input_unregister_device(ft5x06.input);

320 return 0;

321 }

322

323

324 /*

325 * 传统驱动匹配表

326 */

327 static const struct i2c_device_id ft5x06_ts_id[] = {

328 { "edt-ft5206", 0, },

329 { "edt-ft5426", 0, },

330 { /* sentinel */ }

331 };

332

333 /*

334 * 设备树匹配表

335 */

336 static const struct of_device_id ft5x06_of_match[] = {

337 { .compatible = "edt,edt-ft5206", },

338 { .compatible = "edt,edt-ft5426", },

339 { /* sentinel */ }

340 };

341

342 /* i2c 驱动结构体 */

343 static struct i2c_driver ft5x06_ts_driver = {

344 .driver = {

345 .owner = THIS_MODULE,

346 .name = "edt_ft5x06",

347 .of_match_table = of_match_ptr(ft5x06_of_match),

348 },

349 .id_table = ft5x06_ts_id,

350 .probe = ft5x06_ts_probe,

351 .remove = ft5x06_ts_remove,

352 };

353

354 /*

355 * @description : 驱动入口函数

356 * @param : 无

357 * @return : 无

358 */

359 static int __init ft5x06_init(void)

360 {

361 int ret = 0;

362

363 ret = i2c_add_driver(&ft5x06_ts_driver);

364

365 return ret;

366 }

367

368 /*

369 * @description : 驱动出口函数

370 * @param : 无

371 * @return : 无

372 */

373 static void __exit ft5x06_exit(void)

374 {

375 i2c_del_driver(&ft5x06_ts_driver);

376 }

377

378 module_init(ft5x06_init);

379 module_exit(ft5x06_exit);

380 MODULE_LICENSE("GPL");

381 MODULE_AUTHOR("topeet");

第 29~36 行,定义一个设备结构体,存放多点电容触摸设备相关属性信息。

第 38 行,定义一个名为 ft5x06 的全局变量,变量类型就是上面定义的 ft5x06_dev 结构体。

第 46~63 行,ft5x06_ts_reset 函数,用于初始化 FT5426 触摸芯片,其实就是设置 FT5426 的复位 IO为高电平,防止芯片复位。注意在第 52 行使用 devm_gpio_request_one 函数来申请复位 IO,关于“devm_”前缀的作用已经在前面做了详细的讲解。使用“devm_”前缀的 API 函数申请的资源不需要我们手动释放,内核会处理,所以这里使用 devm_gpio_request_one 函数申请 IO 以后不需要我们在卸载驱动的时候手动去释放此 IO。

第 73~98 行,ft5x06_read_regs 函数,用于连续的读取 FT5426 内部寄存器数据,就是 I2C 读取函数。

第 98~124 行,ft5x06_write_regs 函数,用于向 FT5426 寄存器写入连续的数据,也就是 I2C 写函数。

第 133~138 行,ft5x06_write_reg 函数,对 ft5x06_write_regs 函数的简单封装,向 FT5426 指定寄存器写入一个数据,用于配置 FT5426。

第 146~207 行,ft5x06_handler 函数,触摸屏中断服务函数,触摸点坐标的上报就是在此函数中完成的。第 162 行通过 ft5x06_read_regs 函数读取 FT5426 的所有触摸点信息寄存器数据,从 0X02 这个地址开始,一共 29 个寄存器。第 168~199 行的 for 循环就是一个一个的上报触摸点坐标数据,使用 Type B

时序,这个我们已经在前面说了很多次了。最后在 202 行通过 input_sync 函数上报 SYN_REPORT 事件。如果理解了前面讲解的 Type B 时序,那么此函数就很好看懂。

第 215~241 行 , ft5x06_ts_irq 函数,初始化 FT5426 的中断 IO ,第 221 行使用devm_gpio_request_one 函数申请中断 IO。第 232 行使用函数 devm_request_threaded_irq 申请中断,中断处理函数为 ft5x06_handler。

第 250~307 行,当 I2C 设备与驱动匹配以后此函数就会执行,一般在此函数中完成一些初始化工作。我们重点来看一下 277~299 行是关于 input_dev 设备的初始化,第 277~284 行申请并简单的初始化input_dev。第 286 和 288 行设置 input_dev 需要上报的事件为 EV_KEY 和 EV_ABS,需要上报的按键码为BTN_TOUCH。EV_KEY 是按键事件,用于上报触摸屏是否被按下,相当于把触摸屏当做一个按键。EV_ABS 是触摸点坐标数据,BTN_TOUCH 表示将触摸屏的按下和抬起用作 BTN_TOUCH 按键。第 290~293 行调用input_set_abs_params 函 数 设 置 EV_ABS 事 件 需 要 上 报 ABS_X 、 ABS_Y 、 ABS_MT_POSITION_X 和ABS_MT_POSITION_Y。单点触摸需要上报 ABS_X 和 ABS_Y,对于多点触摸需要上报 ABS_MT_POSITION_X 和

ABS_MT_POSITION_Y。第 294 行调用 input_mt_init_slots 函数初始化 slots,也就是最大触摸点数量,FT5426是个 5 点电容触摸芯片,因此一共 5 个 slot。最后在 299 行调用 input_register_device 函数向系统注册input_dev。

第 314~319 行,当卸载驱动的时候 ft5x06_ts_remove 函数就会执行,因为前面很多资源我们都是用“devm_”前缀函数来申请的,因此不需要手动释放。此函数只需要调用 input_unregister_device 来释放掉前面添加到内核中的 input_dev。

第 320 行~结束,剩下的就是 I2C 驱动框架那一套。

相关文章

网友评论

      本文标题:迅为IMX6ULL开发板Linux下电容触摸屏实验-实验程序编写

      本文链接:https://www.haomeiwen.com/subject/gnhrwktx.html