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

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


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

二、硬件原理图分析

三、实验程序编写

1、修改设备树

1)、添加FT5426所使用的IO

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

        检查有没有被占用。

2)、添加FT5426节点

 &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 };

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

1 #include <linux/module.h>
2 #include <linux/ratelimit.h>
......
15 #include <linux/i2c.h>
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、编译驱动程序

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

2、运行测试

depmod //第一次加载驱动的时候需要运行此命令
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驱动文件

八、总结


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/158571.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为认证 | HCIE考证流程详解!

HCIE&#xff08;Huawei Certified ICT Expert&#xff0c;华为认证ICT专家&#xff09;是华为认证体系中最高级别的ICT技术认证&#xff0c;旨在打造高含金量的专家级认证&#xff0c;为技术融合背景下的ICT产业提供新的能力标准&#xff0c;以实现华为认证引领ICT行业技术认证…

优化3种教学方法

在教育领域&#xff0c;教学方法对于学生的学习成果和兴趣至关重要。 第一种是项目式学习。这种方法鼓励学生通过完成实际的项目来获取知识&#xff0c;而不仅仅是在课堂上听讲。学生需要在实际操作中解决问题&#xff0c;这能培养他们的创新思维和实践能力。项目式学习还能提高…

LiteOS同步实验(实现生产者-消费者问题)

效果如下图&#xff1a; 给大家解释一下上述效果&#xff1a;在左侧&#xff08;顶格&#xff09;的是生产者&#xff08;Producer&#xff09;&#xff1b;在右侧&#xff08;空格&#xff09;的是消费者&#xff08;Consumer&#xff09;。生产者有1个&#xff0c;代号为“0”…

阿里云高校计划学生认证领无门槛代金券和教师验证方法

阿里云高校计划扫码完成学生验证即可领取300元无门槛代金券&#xff0c;还可领取3折优惠折扣&#xff0c;适用于云服务器等全量公共云产品&#xff0c;订单原价金额封顶5000元/年&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云高校计划入口及学生认证说明&#xff1a; …

C#核心笔记——(二)C#语言基础

一、C#程序 1.1 基础程序 using System; //引入命名空间namespace CsharpTest //将以下类定义在CsharpTest命名空间中 {internal class TestProgram //定义TestProgram类{public void Test() { }//定义Test方法} }方法是C#中的诸多种类的函数之一。另一种函数*&#xff0c;还…

PyCharm 配置sqlite3驱动下载问题

单击View -> Tool Windows -> Database&#xff0c;打开Database窗体&#xff0c;之后进行配置&#xff0c;下载驱动包失败&#xff01; 解决 &#xff08;1&#xff09;下载Sqlite3驱动 下载地址: Central Repository: org/xerial/sqlite-jdbc 选择的版本是3.34.0,下载…

YB2402系列是一个同步的步骤来自高压输入电源的调节器。

描述&#xff1a; YB2402系列 SOP-8内置mos 具电流限制 输出可调 极简外围 输入耐压32V 输出电流 2A 2.8A 2.5A 3.2A的高效同步车充降压IC。 YB2402C是一个同步的步骤 来自高压输入电源的调节器。 在8V的输入电压范围内工作 至30V 连续电流为2.5A 具有良好负载和线路的输出电流…

小红书运营怎么做,快速提升品牌印象

小红书作为一款流行的社交电商平台&#xff0c;为品牌和创作者提供了宝贵的机会来推广新产品。若你刚刚开始运营小红书账号&#xff0c;今天为大家分享一下小红书运营怎么做&#xff0c;快速提升品牌印象&#xff01; 常言道&#xff0c;万事开头难&#xff0c;但是这一件事也从…

【Linux工具系列】linux安装Maven3.6.3

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

​​【项目实战】犬只牵绳智能识别:源码详细解读与部署步骤

1.识别效果展示 2.视频演示 [YOLOv7]基于YOLOv7的犬只牵绳检测系统(源码&#xff06;部署教程)_哔哩哔哩_bilibili 3.YOLOv7算法简介 YOLOv7 在 5 FPS 到 160 FPS 范围内&#xff0c;速度和精度都超过了所有已知的目标检测器 并在 V100 上&#xff0c;30 FPS 的情况下达到实…

Qt 信号和槽

目录 概念 代码 mainwindow.h me.h xiaohuang.h main.cc mainwindow.cc me.cc xianghuang.cc mainwindow.ui 自定义信号的要求和注意事项: 自定义槽的要求和注意事项: 概念 信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽&#xff0c;实际就是观察者模式(发布-订…

Oracle 的 Java SE、OpenJDK、Database 链接

1 访问主站 Oracle | Cloud Applications and Cloud Platform 2 开发者 2.1 OpenJDK (这里的不用登录&#xff0c;就可以下载) JDK Builds from Oracle 2.2 JavaSE (需要登录&#xff0c;才可以下载) Java Downloads | Oracle 2.3 DataBase (MySQL为例) MySQL :: MySQL Dow…

JavaDS —— 初识集合框架 + 时间/空间复杂度

目录 1. 初识集合框架 1.1 集合框架的初识 1.2 什么是数据结构&#xff1f; 2. 时间与空间复杂度 2.1 时间复杂度 2.2 大O的渐进表示法 2.3 常见时间复杂度计算举例 2.4 空间复杂度 1. 初识集合框架 1.1 集合框架的初识 什么叫集合&#xff1f;什么叫框架&#xff1f;什么又叫集…

webGL开发微信小游戏

WebGL 是一种用于在浏览器中渲染 2D 和 3D 图形的 JavaScript API。微信小游戏本质上是在微信环境中运行的基于 Web 技术的应用&#xff0c;因此你可以使用 WebGL 来开发小游戏。以下是基于 WebGL 开发微信小游戏的一般步骤&#xff0c;希望对大家有所帮助。北京木奇移动技术有…

redis之数据类型

&#xff08;一&#xff09;关系型数据库和非关系型数据库的区别 1、关系型数据库是一个机构化的数据库&#xff0c;列和行 &#xff08;1&#xff09;列&#xff1a;声明对象 &#xff08;2&#xff09;行&#xff1a;记录对象的属性 &#xff08;3&#xff09;表与表之间…

深度学习环境配置(Anaconda+pytorch+pycharm+cuda)

NVIDIA驱动安装 首先查看电脑的显卡版本&#xff0c;步骤为&#xff1a;此电脑右击-->管理-->设备管理器-->显示适配器。就可以看到电脑显卡的版本了。 然后按照电脑信息&#xff0c;到地址 去安装相应的驱动&#xff0c;Notebooks是笔记本的意思&#xff0c;然后下…

数据结构与算法【堆】的Java实现

前言 之前已经说过堆的特点了&#xff0c;具体文章在数据结构与算法【队列】的Java实现-CSDN博客。因此直接实现堆的其他功能。 建堆 所谓建堆&#xff0c;就是将一个初始的堆变为大顶堆或是小顶堆。这里以大顶堆为例。展示如何建堆。 找到最后一个非叶子节点从后向前&…

ChatGPT 使用入门

背景 ChatGPT是一个强大的聊天机器人助手&#xff0c;内置了大量的互联网知识文档&#xff0c;且具有上下文记忆&#xff0c;可以帮我们快速地查找一些资料&#xff0c;了解一个知识&#xff0c;帮我们回答问题&#xff0c;编写代码等。此外&#xff0c;在使用ChatGPT时具有一…

二百零四、Flume——登录监听窗口报错Ncat: bind to :::44444: Address already in use. QUITTING.

一、目的 Flume安装好后测试开启监听窗口44444&#xff0c;结果报错Ncat: bind to :::44444: Address already in use. QUITTING. 二、报错详情 Ncat: bind to :::44444: Address already in use. QUITTING. 三、报错原因 经过分析发现&#xff0c;44444窗口已经被占用 […

多线程访问资源计数不正确问题分析

线程1&#xff1a;首先修改状态为-1&#xff0c;然后分配资源&#xff0c;资源分配成功后&#xff0c;修改状态为0 线程2&#xff1a;查询状态为-1&#xff0c;然后分配资源&#xff0c;资源分配成功后&#xff0c;修改状态为0 存在这种情况&#xff0c;在线程1修改状态为-1时&…