当前位置: 首页 > news >正文

input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序

往期内容

本专栏往期内容:

  1. input子系统的框架和重要数据结构详解-CSDN博客
  2. input device和input handler的注册以及匹配过程解析-CSDN博客
  3. input device和input handler的注册以及匹配过程解析-CSDN博客
  4. 编写一个简单的Iinput_dev框架-CSDN博客
  5. GPIO按键驱动分析与使用:input_dev层-CSDN博客

I2C子系统专栏:

  1. 专栏地址:IIC子系统_憧憬一下的博客-CSDN博客
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有往期内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树_憧憬一下的博客-CSDN博客
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    – 末篇,有往期内容观看顺序


目录

  • 往期内容
  • 前言
  • 1. 驱动程序框架
  • 2. 设备树示例
  • 3. 驱动程序分析
    • 3.1 分配/设置/注册input_dev
    • 3.2 注册中断处理函数
    • 3.3 中断处理函数分析
  • 4.编写

前言

  • Linux 4.x内核

    • Documentation\devicetree\bindings\input\touchscreen\goodix.txt
    • drivers/input/touchscreen/gt9xx/gt9xx.c 📎gt9xx.c
  • 设备树

    • IMX6ULL:Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dts

本节主要讲解内核中提供的gt9xx.c,来看看是如何通过input子系统来实现一个IIC协议的传输,之后在此基础上编写一个简单的input子系统的IIC驱动

1. 驱动程序框架

img

2. 设备树示例

&i2c2 {gt9xx@5d {compatible = "goodix,gt9xx";reg = <0x5d>;status = "okay";interrupt-parent = <&gpio1>;interrupts = <5 IRQ_TYPE_EDGE_FALLING>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_tsc_reset &pinctrl_touchscreen_int>;/*pinctrl-1 = <&pinctrl_tsc_irq>;*//*pinctrl-names = "default", "int-output-low", "int-output-high", "int-input";pinctrl-0 = <&ts_int_default>;pinctrl-1 = <&ts_int_output_low>;pinctrl-2 = <&ts_int_output_high>;pinctrl-3 = <&ts_int_input>;*/reset-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;irq-gpios = <&gpio1 5 IRQ_TYPE_EDGE_FALLING>;irq-flags = <2>;                /*1:rising 2: falling*/touchscreen-max-id = <5>;touchscreen-size-x = <800>;touchscreen-size-y = <480>;touchscreen-max-w = <1024>;touchscreen-max-p = <1024>;/*touchscreen-key-map = <172>, <158>;*/ /*KEY_HOMEPAGE, KEY_BACK*/goodix,type-a-report = <0>;goodix,driver-send-cfg = <0>;goodix,create-wr-node = <1>;goodix,wakeup-with-reset = <0>;goodix,resume-in-workqueue = <0>;goodix,int-sync = <0>;goodix,swap-x2y = <0>;goodix,esd-protect = <0>;goodix,pen-suppress-finger = <0>;goodix,auto-update = <0>;goodix,auto-update-cfg = <0>;goodix,power-off-sleep = <0>;/* ...... */};
};	

这段设备树(Device Tree)定义配置了一个基于 I2C 总线的 Goodix GT9xx 系列触摸屏设备,设备地址为 0x5d

1. 节点和基础属性

&i2c2 {gt9xx@5d {compatible = "goodix,gt9xx";reg = <0x5d>;
  • &i2c2:指向 I2C 总线控制器的节点,这意味着该设备挂载在 I2C 总线 2 上。
  • gt9xx@5d:表示该触摸屏设备的 I2C 地址为 0x5d
  • compatible = "goodix,gt9xx";:指定设备兼容字符串,匹配 Goodix 的 GT9xx 系列触摸屏设备驱动程序。
  • reg = <0x5d>;:设备的 I2C 地址,定义为 0x5d

2. 中断与引脚控制配置

        status = "okay";interrupt-parent = <&gpio1>;interrupts = <5 IRQ_TYPE_EDGE_FALLING>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_tsc_reset &pinctrl_touchscreen_int>;
  • status = "okay";:设备状态,标记为启用。
  • interrupt-parent = <&gpio1>;:指定中断的父节点为 gpio1,即 GPIO 控制器 1。
  • interrupts = <5 IRQ_TYPE_EDGE_FALLING>;:中断配置,使用 GPIO1 控制器的第 5 个引脚,触发模式为下降沿触发(IRQ_TYPE_EDGE_FALLING)。
  • pinctrl-names pinctrl-0:配置引脚控制器,pinctrl-names 指定了配置的名字,pinctrl-0 则定义了相关引脚配置(pinctrl_tsc_resetpinctrl_touchscreen_int),用于触摸屏重置和中断引脚初始化。

3. GPIO 和中断标志

        reset-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;irq-gpios = <&gpio1 5 IRQ_TYPE_EDGE_FALLING>;irq-flags = <2>; 
  • reset-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;:触摸屏的复位引脚定义在 gpio5 的第 2 引脚,信号低电平有效。
  • irq-gpios = <&gpio1 5 IRQ_TYPE_EDGE_FALLING>;:触摸屏的中断引脚配置为 gpio1 控制器的第 5 引脚,设置为下降沿触发。
  • irq-flags = <2>;:中断触发标志,2 表示下降沿触发。

4. 触摸屏参数

        touchscreen-max-id = <5>;touchscreen-size-x = <800>;touchscreen-size-y = <480>;touchscreen-max-w = <1024>;touchscreen-max-p = <1024>;
  • touchscreen-max-id = <5>;:最大触摸点数为 5,表示最多支持 5 个手指的多点触控。
  • touchscreen-size-x touchscreen-size-y:触摸屏 X、Y 轴的分辨率,分别为 800 和 480。
  • touchscreen-max-w touchscreen-max-p:触控宽度和压力的最大值,均为 1024。

5. Goodix 驱动特性

        goodix,type-a-report = <0>;goodix,driver-send-cfg = <0>;goodix,create-wr-node = <1>;goodix,wakeup-with-reset = <0>;goodix,resume-in-workqueue = <0>;goodix,int-sync = <0>;goodix,swap-x2y = <0>;goodix,esd-protect = <0>;goodix,pen-suppress-finger = <0>;goodix,auto-update = <0>;goodix,auto-update-cfg = <0>;goodix,power-off-sleep = <0>;
  • Goodix 特定属性goodix,xxx 属性定义了一些特性和行为,例如:

    • goodix,type-a-reportgoodix,driver-send-cfg:控制是否启用 A 类型报告和驱动是否发送配置信息。
    • goodix,create-wr-node:创建 WR(写)节点的标志。
    • goodix,wakeup-with-resetgoodix,power-off-sleep:控制唤醒和电源管理的行为。
    • 其他 Goodix 触摸屏特性配置,按需要启用或禁用。

这些属性通过设备树配置,使系统能够正确初始化并管理触摸屏设备。

3. 驱动程序分析

gt9xx.c

3.1 分配/设置/注册input_dev

img

gtp_proberet = gtp_request_input_dev(ts);ts->input_dev = input_allocate_device();......ret = input_register_device(ts->input_dev);ret = gtp_request_irq(ts);	

3.2 注册中断处理函数

img

ret = request_threaded_irq(ts->client->irq, NULL,gtp_irq_handler,ts->pdata->irq_flags | IRQF_ONESHOT,ts->client->name,ts);

3.3 中断处理函数分析

通过I2C函数( 2c_tansfer)读取数据、上报数据。

img

static irqreturn_t gtp_irq_handler(int irq, void *dev_id)
{struct goodix_ts_data *ts = dev_id;gtp_work_func(ts);return IRQ_HANDLED;
}static void gtp_work_func(struct goodix_ts_data *ts)
{u8 point_state = 0;  // 保存触摸点的状态u8 key_value = 0;    // 保存按键的状态s32 i = 0;           // 循环变量,用于迭代按键事件s32 ret = -1;        // 函数调用返回值static u8 pre_key;   // 保存上一次的按键值,用于识别按键变化struct goodix_point_t points[GTP_MAX_TOUCH_ID]; // 存储触摸点信息// 检查是否正在执行复位操作,若是,则退出函数if (test_bit(PANEL_RESETTING, &ts->flags))return;// 检查工作线程是否启用,若未启用,则退出函数if (!test_bit(WORK_THREAD_ENABLED, &ts->flags))return;// 手势事件处理,检测滑动唤醒功能是否启用,且设备是否处于休眠模式if (ts->pdata->slide_wakeup && test_bit(DOZE_MODE, &ts->flags)) {// 调用手势处理函数 gtp_gesture_handlerret = gtp_gesture_handler(ts);if (ret) {// 若处理失败,记录错误日志dev_err(&ts->client->dev,"Failed handler gesture event %d\n", ret);}return;  // 处理完成后退出函数}// 获取触摸点的状态及按键状态-----------1)point_state = gtp_get_points(ts, points, &key_value); if (!point_state) {// 若未检测到有效的触摸点,记录调试日志dev_dbg(&ts->client->dev, "Invalid finger points\n");return;  // 没有有效触摸点,直接返回}// 处理触摸按键事件if (key_value & 0xf0 || pre_key & 0xf0) {  // 判断是否有按键事件// 处理手写笔的按键switch (key_value) {case 0x40: // 两个按键都按下input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 1);input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 1);break;case 0x10: // 按下第一个按键input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 1);input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 0);dev_dbg(&ts->client->dev, "pen button1 down\n");break;case 0x20: // 按下第二个按键input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 0);input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 1);break;default: // 没有按键按下,恢复初始状态input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 0);input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 0);dev_dbg(&ts->client->dev, "button1 up\n");break;}input_sync(ts->input_dev); // 同步输入事件pre_key = key_value;       // 更新上次按键状态} else if (key_value & 0x0f || pre_key & 0x0f) {  // 判断是否有面板按键事件// 遍历所有按键,检测是否有按键状态发生变化for (i = 0; i < ts->pdata->key_nums; i++) {if ((pre_key | key_value) & (0x01 << i))input_report_key(ts->input_dev,ts->pdata->key_map[i],     // 具体按键映射key_value & (0x01 << i));  // 按键状态}input_sync(ts->input_dev); // 同步输入事件pre_key = key_value;       // 更新上次按键状态}// 选择不同的报告格式进行触摸点的处理if (!ts->pdata->type_a_report)gtp_mt_slot_report(ts, point_state & 0x0f, points);  // Slot方式----------------------(2)elsegtp_type_a_report(ts, point_state & 0x0f, points);   // Type A方式
}

(1)point_state = gtp_get_points(ts, points, &key_value); : 从 I2C 总线上读取触摸点的数据和触摸状态。提取坐标、压力值、工具类型等信息,若需要则交换 X/Y 坐标。 将手写笔和普通触摸点区分,按需要调整触摸点数据。完成数据读取后,通过发送结束命令结束当前触摸事件。 ret = gtp_i2c_read(ts->client, point_data, 12); //这里内部就调用到了i2c_transfer发起传输

static u8 gtp_get_points(struct goodix_ts_data *ts,struct goodix_point_t *points,u8 *key_value)
{int ret;                // 保存 I2C 传输返回状态int i;                  // 循环变量u8 *coor_data = NULL;   // 指向触摸坐标数据的指针u8 finger_state = 0;    // 保存手指状态信息u8 touch_num = 0;       // 保存当前触摸点的数量u8 end_cmd[3] = {       // 用于向触摸屏发送 "结束" 命令GTP_READ_COOR_ADDR >> 8,GTP_READ_COOR_ADDR & 0xFF,0};u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH_ID + 1] = {  // 存储触摸点的数据GTP_READ_COOR_ADDR >> 8,GTP_READ_COOR_ADDR & 0xFF};// 读取触摸点的坐标数据ret = gtp_i2c_read(ts->client, point_data, 12);   //这里内部就调用到了i2c_transfer发起传输if (ret < 0) {dev_err(&ts->client->dev, "I2C transfer error. errno:%d\n ", ret);return 0;  // 读取失败返回0}finger_state = point_data[GTP_ADDR_LENGTH];  // 获取触摸屏状态if (finger_state == 0x00)                    // 若无触摸点return 0;// 获取触摸点数量(低 4 位表示触摸点数量)touch_num = finger_state & 0x0f;// 检查触摸状态是否合法,如超出最大触摸数或没有触摸if ((finger_state & MASK_BIT_8) == 0 || touch_num > ts->pdata->max_touch_id) {dev_err(&ts->client->dev, "Invalid touch state: 0x%x", finger_state);finger_state = 0;  // 若非法则重置触摸状态goto exit_get_point;  // 跳转到退出点}// 若有多个触摸点,则读取后续触摸点数据if (touch_num > 1) {u8 buf[8 * GTP_MAX_TOUCH_ID] = {(GTP_READ_COOR_ADDR + 10) >> 8,(GTP_READ_COOR_ADDR + 10) & 0xff};ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1));if (ret < 0) {dev_err(&ts->client->dev, "I2C error. %d\n", ret);finger_state = 0;goto exit_get_point;}memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));}// 触摸按键事件,低 4 位标识按键事件*key_value = point_data[3 + 8 * touch_num];// 初始化触摸点信息数组memset(points, 0, sizeof(*points) * GTP_MAX_TOUCH_ID);for (i = 0; i < touch_num; i++) {coor_data = &point_data[i * 8 + 3];  // 获取触摸点的坐标数据points[i].id = coor_data[0];         // 触摸点 IDpoints[i].x = coor_data[1] | (coor_data[2] << 8);  // 触摸点的 X 坐标points[i].y = coor_data[3] | (coor_data[4] << 8);  // 触摸点的 Y 坐标points[i].w = coor_data[5] | (coor_data[6] << 8);  // 触摸点的宽度points[i].p = coor_data[5] | (coor_data[6] << 8);  // 压力值// 判断是否需要交换 X/Y 坐标if (ts->pdata->swap_x2y)GTP_SWAP(points[i].x, points[i].y);dev_dbg(&ts->client->dev, "[%d][%d %d %d]\n", points[i].id, points[i].x, points[i].y, points[i].p);// 判断是否为手写笔触摸点if (points[i].id & 0x80) {points[i].tool_type = GTP_TOOL_PEN;points[i].id = 10;  // 手写笔 ID 固定为 10if (ts->pdata->pen_suppress_finger) {points[0] = points[i];memset(++points, 0, sizeof(*points) * (GTP_MAX_TOUCH_ID - 1));finger_state &= 0xf0;finger_state |= 0x01;  // 设置第一个触摸点为手写笔break;}} else {points[i].tool_type = GTP_TOOL_FINGER;}}exit_get_point:// 若未处于 RAW 数据模式,写入结束命令以结束当前触摸事件if (!test_bit(RAW_DATA_MODE, &ts->flags)) {ret = gtp_i2c_write(ts->client, end_cmd, 3);if (ret < 0)dev_info(&ts->client->dev, "I2C write end_cmd error!");}return finger_state;  // 返回触摸屏状态
}

(2)gtp_irq_handler中你的gtp_mt_slot_report(ts, point_state & 0x0f, points); // Slot方式

  • 函数主要负责报告多点触控的触摸点状态,包括其位置、工具类型和压力等。
  • 使用位图(cur_touchpre_touch)跟踪当前和先前的触摸 ID,从而确定哪些触摸点是新触摸、哪些是结束触摸。
static void gtp_mt_slot_report(struct goodix_ts_data *ts, u8 touch_num, struct goodix_point_t *points)
{int i; // 循环变量u16 cur_touch = 0; // 当前触摸 ID 位图static u16 pre_touch; // 上一帧的触摸 ID 位图static u8 pre_pen_id; // 上一次触摸的笔 ID// 遍历每个可能的触摸点,最大触摸 ID 由 ts->pdata->max_touch_id 决定for (i = 0; i < ts->pdata->max_touch_id; i++) {// 检查当前触摸点是否有效if (touch_num && i == points->id) {// 设置当前触摸点的槽(slot)input_mt_slot(ts->input_dev, points->id);// 判断工具类型if (points->tool_type == GTP_TOOL_PEN) {// 如果是笔工具,报告笔的状态为真input_mt_report_slot_state(ts->input_dev, MT_TOOL_PEN, true);pre_pen_id = points->id; // 更新上一次的笔 ID} else {// 否则为手指工具,报告状态为真input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);}// 报告触摸点的 X 坐标input_report_abs(ts->input_dev, ABS_MT_POSITION_X, points->x);// 报告触摸点的 Y 坐标input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, points->y);// 报告触摸点的宽度input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, points->w);// 报告触摸点的压力input_report_abs(ts->input_dev, ABS_MT_PRESSURE, points->p);// 更新当前触摸 ID 位图cur_touch |= 0x01 << points->id;points++; // 移动到下一个触摸点} // 如果在上一帧中这个槽是活跃的else if (pre_touch & (0x01 << i)) {// 设置当前槽为 iinput_mt_slot(ts->input_dev, i);// 检查上次是否是笔 IDif (pre_pen_id == i) {// 如果是笔,报告笔的状态为假input_mt_report_slot_state(ts->input_dev, MT_TOOL_PEN, false);/* 有效的 ID 应小于 10,所以将 ID 设置为 0xff * 来表示无效状态*/pre_pen_id = 0xff; // 重置笔 ID} else {// 否则报告手指状态为假input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);}}}// 更新上一帧的触摸 ID 位图为当前帧pre_touch = cur_touch;// 同步当前的触摸帧input_mt_sync_frame(ts->input_dev);// 同步输入设备状态input_sync(ts->input_dev);
}
gtp_irq_handlergtp_work_func(ts);point_state = gtp_get_points(ts, points, &key_value);gtp_i2c_readi2c_transfergtp_mt_slot_report(ts, point_state & 0x0f, points);input_mt_slotinput_mt_report_slot_stateinput_report_abs

有所疑问可以先看:

I2C相关结构体讲解:i2c_adapter、i2c_algorithm、i2c_msg-CSDN博客

编写一个简单的Iinput_dev框架-CSDN博客

4.编写

这里主要是参考📎gpio_keys.c,但思路其实和上面介绍的差不多,设置Input_dev并注册、配置事件类型和支持的事件、请求中断注册中断等。

#define TOUCHSCREEN_POLL_TIME_MS 10 // 定义轮询时间,单位为毫秒// 定义模拟触摸屏硬件数据结构
struct qemu_ts_con {volatile unsigned int pressure; // 触摸压力,0表示未触摸,非0表示触摸volatile unsigned int x;        // 触摸X坐标volatile unsigned int y;        // 触摸Y坐标volatile unsigned int clean;    // 清除标志
};// 全局变量定义
static struct input_dev *g_input_dev;  // 输入设备
static int g_irq;                      // 中断号
static struct qemu_ts_con *ts_con;     // 指向模拟触摸屏的结构体
struct timer_list ts_timer;            // 定时器,用于轮询触摸状态// 定时器回调函数,用于定期轮询触摸状态
static void ts_irq_timer(unsigned long _data)
{// 如果检测到触摸事件if (ts_con->pressure) {// 报告触摸位置input_event(g_input_dev, EV_ABS, ABS_X, ts_con->x);input_event(g_input_dev, EV_ABS, ABS_Y, ts_con->y);input_sync(g_input_dev);// 重新启动定时器mod_timer(&ts_timer, jiffies + msecs_to_jiffies(TOUCHSCREEN_POLL_TIME_MS));}
}// 中断服务函数,触发触摸事件处理
static irqreturn_t input_dev_demo_isr(int irq, void *dev_id)
{// 如果有触摸事件发生if (ts_con->pressure) {// 报告触摸位置和触摸按键状态input_event(g_input_dev, EV_ABS, ABS_X, ts_con->x);input_event(g_input_dev, EV_ABS, ABS_Y, ts_con->y);input_event(g_input_dev, EV_KEY, BTN_TOUCH, 1);input_sync(g_input_dev);// 启动定时器继续监控触摸状态mod_timer(&ts_timer, jiffies + msecs_to_jiffies(TOUCHSCREEN_POLL_TIME_MS));} else {// 如果没有触摸,报告触摸键松开input_event(g_input_dev, EV_KEY, BTN_TOUCH, 0);input_sync(g_input_dev);}return IRQ_HANDLED;
}// 设备初始化函数,负责资源分配和设备注册
static int input_dev_demo_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;int error;struct resource *io;int gpio;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);// 获取GPIO编号gpio = of_get_gpio(pdev->dev.of_node, 0);// 分配并初始化input_dev结构体g_input_dev = devm_input_allocate_device(dev);if (!g_input_dev)return -ENOMEM;// 配置input_dev的基础信息g_input_dev->name = "input_dev_demo";g_input_dev->phys = "input_dev_demo";g_input_dev->dev.parent = dev;g_input_dev->id.bustype = BUS_HOST;g_input_dev->id.vendor = 0x0001;g_input_dev->id.product = 0x0001;g_input_dev->id.version = 0x0100;// 配置事件类型和支持的事件__set_bit(EV_KEY, g_input_dev->evbit);__set_bit(EV_ABS, g_input_dev->evbit);__set_bit(INPUT_PROP_DIRECT, g_input_dev->propbit);__set_bit(BTN_TOUCH, g_input_dev->keybit);__set_bit(ABS_X, g_input_dev->absbit);__set_bit(ABS_Y, g_input_dev->absbit);// 配置触摸屏的绝对坐标范围input_set_abs_params(g_input_dev, ABS_X, 0, 0xffff, 0, 0);input_set_abs_params(g_input_dev, ABS_Y, 0, 0xffff, 0, 0);// 注册input_deverror = input_register_device(g_input_dev);if (error)return error;// 获取硬件寄存器的I/O资源并映射io = platform_get_resource(pdev, IORESOURCE_MEM, 0);ts_con = ioremap(io->start, resource_size(io));// 获取并请求GPIO的中断号g_irq = gpio_to_irq(gpio);error = request_irq(g_irq, input_dev_demo_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "input_dev_demo_irq", NULL);if (error)return error;// 初始化定时器setup_timer(&ts_timer, ts_irq_timer, (unsigned long)NULL);return 0;
}// 设备卸载函数,释放资源
static int input_dev_demo_remove(struct platform_device *pdev)
{del_timer_sync(&ts_timer);       // 停止定时器iounmap(ts_con);                 // 取消内存映射free_irq(g_irq, NULL);           // 释放中断input_unregister_device(g_input_dev); // 注销设备return 0;
}// 设备树匹配表
static const struct of_device_id input_dev_demo_of_match[] = {{ .compatible = "100ask,input_dev_demo", },{ },
};// 平台驱动结构体定义
static struct platform_driver input_dev_demo_driver = {.probe  = input_dev_demo_probe,.remove = input_dev_demo_remove,.driver = {.name = "input_dev_demo",.of_match_table = input_dev_demo_of_match,}
};// 模块初始化函数
static int __init input_dev_demo_init(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return platform_driver_register(&input_dev_demo_driver);
}// 模块退出函数
static void __exit input_dev_demo_exit(void)
{platform_driver_unregister(&input_dev_demo_driver);
}module_init(input_dev_demo_init);
module_exit(input_dev_demo_exit);MODULE_LICENSE("GPL");

相关文章:

input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序

往期内容 本专栏往期内容&#xff1a; input子系统的框架和重要数据结构详解-CSDN博客input device和input handler的注册以及匹配过程解析-CSDN博客input device和input handler的注册以及匹配过程解析-CSDN博客编写一个简单的Iinput_dev框架-CSDN博客GPIO按键驱动分析与使用&…...

SQL-lab靶场less1-4

说明&#xff1a;部分内容来源于网络&#xff0c;如有侵权联系删除 前情提要&#xff1a;搭建sql-lab本地靶场的时候发现一些致命的报错&#xff1a; 这个程序只能在php 5.x上运行&#xff0c;在php 7及更高版本上&#xff0c;函数“mysql_query”和一些相关函数被删除&#xf…...

【生成模型之二】diffusion model模型

【算法简历修改、职业规划、校招实习咨询请私信联系】 【Latent-Diffusion 代码】 生成模型分类概述 Diffusion Model&#xff0c;这一深度生成模型&#xff0c;源自物理学中的扩散现象&#xff0c;呈现出令人瞩目的创新性。与传统的生成模型&#xff0c;如VAE、GAN相比&…...

记录 Maven 版本覆盖 Bug 的解决过程

背景 在使用 Maven 进行项目管理时&#xff0c;依赖版本的管理是一个常见且重要的环节。最近&#xff0c;在我的项目中遇到了一个关于依赖版本覆盖的 Bug&#xff0c;这个问题导致了 Apollo 框架的版本不一致&#xff0c;影响了项目的正常运行。以下是我解决这个问题的过程记录…...

【K8S系列】Kubernetes Service 基础知识 详细介绍

在 Kubernetes 中&#xff0c;Service 是一种抽象的资源&#xff0c;用于定义一组 Pod 的访问策略。它为这些 Pod 提供了一个稳定的访问入口&#xff0c;解决了 Pod 可能频繁变化的问题。本文将详细介绍 Kubernetes Service 的类型、功能、使用场景、DNS 和负载均衡等方面。 1.…...

python在物联网领域的数据应用分析与实战!

引言 物联网(IoT)是一个快速发展的领域,涉及到各种设备和传感器的连接与数据交换。随着设备数量的激增,数据的产生速度也在不断加快。 如何有效地分析和利用这些数据,成为了物联网应用成功的关键。Python作为一种强大的编程语言,因其简洁易用的特性和丰富的库支持,成为…...

目标跟踪算法-卡尔曼滤波详解

卡尔曼滤波是一种递归的优化算法&#xff0c;用于估计一个系统的动态状态&#xff0c;常用于跟踪、导航、时间序列分析等领域。它的关键在于使用一系列测量数据&#xff08;通常含噪声&#xff09;来估计系统的真实状态&#xff0c;使得估计值更接近实际情况。卡尔曼滤波器适合…...

SpringBoot后端开发常用工具详细介绍——application多环境配置与切换

文章目录 引言介绍application.yml&#xff08;主配置文件&#xff09;application-dev.yml&#xff08;开发环境配置&#xff09;application-test.yml&#xff08;测试环境配置&#xff09;application-prod.yml&#xff08;生产环境配置&#xff09;激活配置文件参考内容 引…...

php反序列化漏洞典型例题

1.靶场环境 ctfhub-技能树-pklovecloud 引用题目&#xff1a; 2021-第五空间智能安全大赛-Web-pklovecloud 2.过程 2.1源代码 启动靶场环境&#xff0c;访问靶场环境&#xff0c;显示源码&#xff1a;直接贴在下面&#xff1a; <?php include flag.php; class pks…...

浅析Android View绘制过程中的Surface

前言 在《浅析Android中View的测量布局流程》中我们对VSYNC信号到达App进程之后开启的View布局过程进行了分析&#xff0c;经过对整个App界面的View树进行遍历完成了测量和布局&#xff0c;确定了View的大小以及在屏幕中所处的位置。但是&#xff0c;如果想让用户在屏幕上看到…...

基于卷积神经网络的大豆种子缺陷识别系统,resnet50,mobilenet模型【pytorch框架+python源码】

更多目标检测和图像分类识别项目可看我主页其他文章 功能演示&#xff1a; 大豆种子缺陷识别系统&#xff0c;卷积神经网络&#xff0c;resnet50&#xff0c;mobilenet【pytorch框架&#xff0c;python源码】_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于卷积神…...

HarmonyOS项目开发一多简介

目录 一、布局能力概述 二、自适应布局 三、响应式布局 四、典型布局场景 一、布局能力概述 布局决定页面元素排布及显示&#xff1a;在页面设计及开发中&#xff0c;布局能力至关重要&#xff0c;主要通过组件结构来确定使用何种布局。 自适应布局与响应式布局&#xff1…...

C++基础三

构造函数 构造函数(初始化类成员变量)&#xff1a; 1、属于类的成员函数之一 2、构造函数没有返回类型 3、构造函数的函数名必须与类名相同 4、构造函数不允许手动调用(不能通过类对象调用) 5、构造函数在类对象创建时会被自动调用 6、如果没有显示声…...

利用ChatGPT完成2024年MathorCup大数据挑战赛-赛道A初赛:台风预测与分析

利用ChatGPT完成2024年MathorCup大数据挑战赛-赛道A初赛&#xff1a;台风预测与分析 引言 在2024年MathorCup大数据挑战赛中&#xff0c;赛道A聚焦于气象数据分析&#xff0c;特别是台风的生成、路径预测、和降水风速特性等内容。本次比赛的任务主要是建立一个分类评价模型&…...

Linux系统操作篇 one -文件指令及文件知识铺垫

Linux操作系统入门-系统篇 前言 Linux操作系统与Windows和MacOS这些系统不同&#xff0c;Linux是黑屏的操作系统&#xff0c;操作方式使用的是指令和代码行来进行&#xff0c;因此相对于Windows和MacOS这些带有图形化界面的系统&#xff0c;Linux的入门门槛和上手程度要更高&…...

隨筆20241028 ISR 的收缩与扩展及其机制解析

在 Kafka 中&#xff0c;ISR&#xff08;In-Sync Replicas&#xff09; 是一组副本&#xff0c;它们与 Leader 保持同步&#xff0c;确保数据一致性。然而&#xff0c;ISR 的大小会因多种因素而变化&#xff0c;包括收缩和扩展。以下是 ISR 收缩与扩展的详细解释及其背后的机制…...

linux-字符串相关命令

1、cut 提取文件每一行中的内容 下面是一些常用的 cut 命令选项的说明&#xff1a; -c, --characters列表&#xff1a;提取指定字符位置的数据。-d, --delimiter分界符&#xff1a;指定字段的分隔符&#xff0c;默认为制表符。-f, --fieldsLIST&#xff1a;提取指定字段的数据…...

ES6 函数的扩展

ES6 之前&#xff0c;不能直接为函数的参数指定默认值&#xff0c;只能采用变通的方法 ES6 允许为函数的参数设置默认值&#xff0c;即直接写在参数定义的后面 参数变量是默认声明的&#xff0c;所以不能用 let 或 const 再次声明 使用参数默认值时&#xff0c;函数不能有同名参…...

Mac 查看占用特定端口、终止占用端口的进程

在 macOS 上&#xff0c;可以使用以下命令来查看占用特定端口&#xff08;例如 8080&#xff09;的进程&#xff1a; lsof -i :8080命令说明 lsof&#xff1a;列出打开的文件和网络连接信息。-i :8080&#xff1a;筛选出正在监听 8080 端口的进程。 输出结果结构 执行上述命…...

C#入坑JAVA MyBatis入门 CURD 批量 联表分页查询

本文&#xff0c;分享 MyBatis 各种常用操作&#xff0c;不限于链表查询、分页查询等等。 1. 分页查询 在 下文的 的「3.4 selectPage」小节&#xff0c;我们使用 MyBatis Plus 实现了分页查询。除了这种方式&#xff0c;我们也可以使用 XML 实现分页查询。 这里&#xff0c…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

MySQL 8.0 事务全面讲解

以下是一个结合两次回答的 MySQL 8.0 事务全面讲解&#xff0c;涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容&#xff0c;并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念&#xff08;ACID&#xff09; 事务是…...