Linux驱动开发(13):输入子系统–按键输入实验

news2024/12/15 18:54:21

计算机的输入设备繁多,有按键、鼠标、键盘、触摸屏、游戏手柄等等,Linux内核为了能够将所有的输入设备进行统一的管理, 设计了输入子系统。为上层应用提供了统一的抽象层,各个输入设备的驱动程序只需上报产生的输入事件即可。

下面以按键中断为例讲解输入子系统的使用。 本章配套源码和设备树插件位于“ ~/linux_driver/input_sub_system ”目录下。

1. 输入子系统简介

linux为了统一各个输入设备,将输入子系统分为了Drivers(驱动层)、Input Core(输入子系统核心层)、 handlers(事件处理层)三部分。

1

  • Drivers主要实现对硬件设备的读写访问,设置中断,并将硬件产生的事件转为Input Core定义的规范提交给Handlers;

  • Input Core起到承上启下的作用,为Drivers提供了规范及接口,并通知Handlers对事件进行处理;

  • Handlers并不涉及硬件方面的具体操作,是一个纯软件层,包含了不同的解决方案,如按键、键盘、鼠标、游戏手柄等。

最终所有输入设备的输入信息将被抽象成以下结构体:

struct input_event结构体 (内核源码/include/uapi/linux/input.h):

//输入事件
struct input_event{
    struct timeval time;   //事件产生的时间
    __u16 type;            //输入设备的类型,鼠标、键盘、触摸屏
    __u16 code;
    __s16 value;
}
  • time :事件产生的时间。

  • type :输入设备的类型。

  • code :根据设备类型的不同而含义不同,如果设备类型是按键,code表示为按键值(如第几个按键等)。

  • value:根据设备类型的不同而含义不同。如果设备类型是按键,value表示的是松开或者按下。

本章目的是编写一个基于输入子系统和中断的按键驱动程序,重点在于了解Input Core为我们提供了哪些接口,并了解如何将 按键信息以事件上报。

input子系统Input Core实现代码是“ 内核源码/drivers/input/input.c ”以及“ 内核源码/include/linux/input.h ”两个文件 为我们提供了注册输入子系统的API,通过操作这些API就可以实现输入事件的注册、初始化、上报、注销等等工作。 下面我们介绍输入子系统常用的API接口及数据结构。

1.1. input_dev结构体

在输入子系统中input_dev代表一个具体的输入设备,后面将会根据具体的设备来初始化这个结构体,结构体成员介绍如下: (input_dev参数很多,有些不需要我们手动配置,所以这里只列出和介绍常用的参数,完整内容位于input.h文件)。

input_dev结构体 (内核源码/include/linux/input.h):

struct input_dev {
    const char *name;  //提供给用户的输入设备的名称
    const char *phys;  //提供给编程者的设备节点的名称
    const char *uniq;   //指定唯一的ID号
    struct input_id id; //输入设备标识ID

    unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];

    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];  //指定设备支持的事件类型
    unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //记录支持的键值
    unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //记录支持的相对坐标位图
    unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //记录支持的绝对坐标位图

    /*----------以下结构体成员省略----------------*/
};

结构体成员中最重要的是 evbitkeybitrelbit 等数组,这些数组设置了设备输入事件的类型和键值。

  • evbit:用于指定支持的事件类型,这要根据实际输入设备能够产生的事件来选择,可选选项如下所示。

输入子系统事件类型 (内核源码/include/uapi/linux/input-event-codes.h):

#define EV_SYN                      0x00 //同步事件
#define EV_KEY                      0x01 //用于描述键盘、按钮或其他类似按键的设备。
#define EV_REL                      0x02 //用于描述相对位置变化,例如鼠标移动
#define EV_ABS                      0x03 //用于描述绝对位置变化,例如触摸屏的触点坐标
#define EV_MSC                      0x04 //其他事件类型
#define EV_SW                       0x05 //用于描述二进制开关类型的设备,例如拨码开关。
#define EV_LED                      0x11
#define EV_SND                      0x12
#define EV_REP                      0x14
#define EV_FF                       0x15
#define EV_PWR                      0x16
#define EV_FF_STATUS                0x17
#define EV_MAX                      0x1f
#define EV_CNT                      (EV_MAX+1)

上面代码中前几个宏定义较为常用的输入事件类型,介绍如代码后面所示。 完整的介绍可以参考内核源码目录下的“ 内核源码/Documentation/input/event-codes.rst ”内核文档。 很明显,我们这章节要使用的按键的事件类型应该使用 EV_KEY 。

  • keybit:记录支持的键值,“键值”在程序中用于区分不同的按键,可选“键值”如下所示。

输入子系统—按键键值 (内核源码/include/uapi/linux/input-event-codes.h):

#define KEY_RESERVED                0
#define KEY_ESC                     1
#define KEY_1                       2
#define KEY_2                       3
#define KEY_3                       4
#define KEY_4                       5
/*-----------以下内容省略-------------*/

可以看出“键值”就是一些数字。只要实际设备与按键对应即可。例如本章的按键可以使用KEY_1、也可以使用KEY_4等。

  • relbit、absbit:这两个参数和上面的keybit都和参数evbit有关,如果evbit中只选择了EV_KEY, 那么我们就不需要设置relbit(相对坐标)和absbit(绝对坐标)以及后面省略成员的内容。这些内容使用到时再具体介绍。 使用不同的输入事件类型需要设备不同的

总之,input_dev结构体成员很多,但是对应到一个具体的输入设备,只需要设置自己用到的其中一两个属性。

1.2. input_dev结构体的申请和释放

一个input_dev结构体代表了一个输入设备,它实际会占输入子系统的一个次设备号。 input子系统为我们提供了申请和释放input_dev结构体的函数。 由于input_dev结构体的成员很多,初始化过程也相对麻烦,一般都使用input子系统为我们提供的接口函数来 申请和释放input_dev结构体,如下所示。

input_dev申请函数 (内核源码/drivers/input/input.c):

struct input_dev *input_allocate_device(void)
{
    static atomic_t input_no = ATOMIC_INIT(-1);
    struct input_dev *dev;

    dev = kzalloc(sizeof(*dev), GFP_KERNEL);        //动态内存申请
    if (dev) {
        dev->dev.type = &input_dev_type;
        dev->dev.class = &input_class;                      //dev->dev为struct device类型结构体
        device_initialize(&dev->dev);                       //初始化dev->dev结构体内部成员
        mutex_init(&dev->mutex);                            //初始化互斥锁
        spin_lock_init(&dev->event_lock);           //初始化自旋锁
        timer_setup(&dev->timer, NULL, 0);          //初始化定时器
        INIT_LIST_HEAD(&dev->h_list);                       //初始化handle链表节点
        INIT_LIST_HEAD(&dev->node);                         //初始化输入设备链表节点

        dev_set_name(&dev->dev, "input%lu",
                (unsigned long)atomic_inc_return(&input_no));  //设置设备名称

        __module_get(THIS_MODULE);
    }

    return dev;
}
EXPORT_SYMBOL(input_allocate_device);

我们只需要知道如何调用这个函数来申请input_dev即可,想要更深入学习的同学们可以尝试去分析整个输入子系统的实现源码, 对于输入子系统的源码分析就可以写一篇很长的文章了,这里并不展开详细的源码分析。

input_dev释放函数 (内核源码/drivers/input/input.c):

void input_free_device(struct input_dev *dev)

申请和释放函数接口比较简单。申请函数input_allocate_device执行成功后会返回申请得到的input_dev结构体的地址, 如果失败,返回NULL。释放函数input_free_device只有一个参数dev,用于指定要释放的input_dev结构体。

1.3. 注册和注销input_dev结构体

input_dev申请成功后,我们需要根据自己的实际输入设备配置input_dev结构体,具体配置在实验代码编写部分会详细说明, 配置完成后还要使用注册和注销函数将input_dev注册到输入子系统。注册和注销函数如下:

input_dev注册函数(内核源码/drivers/input/input.c):

int input_register_device(struct input_dev *dev)

参数: dev:struct input_dev类型指针 

返回值:

  • 成功: 0

  • 失败: 返回非0值

input_register_device函数将输入设备(input_dev)注册到输入子系统的核心层。 该函数使用需要注意以下几点

  • 使用该函数注册的input_dev必须是使用input_allocate_device函数申请得到的。

  • 注册之前需要根据实际输入设备配置好input_dev结构体。

  • 如果注册失败必须调用input_free_device函数释放input_dev结构体。

  • 如果注册成功,在函数退出时只需要使用input_unregister_device函数注销input_dev结构体不需要再调用 input_free_device函数释放input_dev结构体。

input_dev注销函数 (内核源码/drivers/input/input.c):

void input_unregister_device(struct input_dev *dev)

input_unregister_device是注销函数,输入子系统的资源是有限的,不使用是应当注销。 调用input_unregister_device注销函数之后就不必调用input_free_device函数释放input_dev。

1.4. 上报事件函数和上报结束函数

以按键为例,按键按下后需要使用上报函数向输入子系统核心层上报按键事件,并且上报后还要发送上报结束信息。函数定义如下所示。

通用的上报事件函数 (内核源码include/linux/input.h):

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);

参数

  • dev,指定输入设备(input_dev结构体)。

  • type,事件类型。我们在根据实际输入设备配置input_dev结构体时会设置input_dev-> evbit参数, 用于设置输入设备能够产生的事件类型(可能是多个)。上报事件时要从“能够产生”的这些事件类型中选择。

  • code,编码。以按键为例,按键的编码就是我们设置的按键键值。

  • value,指定事件的值。

返回值: 

上报按键事件及发送上报结束事件 (内核源码include/linux/input.h):

static inline void input_sync(struct input_dev *dev)
{
    input_event(dev, EV_SYN, SYN_REPORT, 0);
}

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
    input_event(dev, EV_KEY, code, !!value);
}

input子系统为不同的输入事件函数提供了不同的函数接口,这些函数接口只是对input_event函数进行简单的封装, 具体的参数参照input_event函数。input_report_key用于上报按键事件,input_sync用于发送同步信号,表示上报结束。

2. 输入子系统实验

本小节以按键为例介绍输入子系统的具体使用方法。本实验在上一章“中断实验”基础上完成。结合源码介绍如下。

2.1. 设备树插件实现

设备树插件与上一章的“按键中断实验”使用的设备树插件几乎相同,我们只需要将中断类型修改为“上升和下降沿触发”。 修改部分如下所示。

/dts-v1/;
/plugin/;
#include "../imx6ul-pinfunc.h"
#include "dt-bindings/interrupt-controller/irq.h"
#include "dt-bindings/gpio/gpio.h"

/ {
    fragment@0 {
        target-path = "/";
        __overlay__ {
            button_interrupt {
                    compatible = "button_interrupt";
                    pinctrl-names = "default";
                    pinctrl-0 = <&pinctrl_button>;
                    button_gpio = <&gpio5 1 GPIO_ACTIVE_LOW>;
                    status = "okay";
                    interrupt-parent = <&gpio5>;
                    interrupts = <1 IRQ_TYPE_EDGE_BOTH>;    //设备树修改内容,将上升沿触发修改为双边沿触发
            };
        };
    };


    fragment@1 {
        target = <&iomuxc>;
        __overlay__ {
        pinctrl_button: buttongrp {
                            fsl,pins = <
                                    MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01  0x10b0
                            >;
                    };
        };
    };
};

修改内容很简单只是将原来中断的触发方式修改为双边沿触发,其他的设备树内容不变。

2.2. 驱动程序实现

2.2.1. 驱动入口函数

驱动入口函数如下所示。

static int __init button_driver_init(void)
{

    int error;
    printk(KERN_ERR "button_driver_init \n");

    /*-----------第一部分-------------*/
    /*获取按键 设备树节点*/
    button_device_node = of_find_node_by_path("/button_interrupt");
    if (NULL == button_device_node)
    {
            printk(KERN_ERR "of_find_node_by_path error!");
            return -1;
    }

    /*获取按键使用的GPIO*/
    button_GPIO_number = of_get_named_gpio(button_device_node, "button_gpio", 0);
    if (0 == button_GPIO_number)
    {
            printk(KERN_ERR"of_get_named_gpio error");
            return -1;
    }

    /*申请GPIO  , 记得释放*/
    error = gpio_request(button_GPIO_number, "button_gpio");
    if (error < 0)
    {
            printk(KERN_ERR "gpio_request error");
            gpio_free(button_GPIO_number);
            return -1;
    }

    error = gpio_direction_input(button_GPIO_number); //设置引脚为输入模式


    /*获取中断号*/
    interrupt_number = irq_of_parse_and_map(button_device_node, 0);
    printk(KERN_ERR "\n interrupt_number =  %d \n", interrupt_number);

    /*申请中断, 记得释放*/
    error = request_irq(interrupt_number, button_irq_hander, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "button_interrupt", NULL);
    if (error != 0)
    {
            printk(KERN_ERR "request_irq error");
            gpio_free(button_GPIO_number);
            free_irq(interrupt_number, NULL);
            return -1;
    }

    /*-----------第二部分-------------*/
    /*申请输入子系统结构体*/
    button_input_dev = input_allocate_device();
    if (NULL == button_input_dev)
    {
            printk(KERN_ERR "input_allocate_device error");
            return -1;
    }
    button_input_dev->name = BUTTON_NAME;

    /*-----------第三部分-------------*/
    /*设置要使用的输入事件类型*/
    button_input_dev->evbit[0] = BIT_MASK(EV_KEY);
    input_set_capability(button_input_dev, EV_KEY, KEY_1); //标记设备能够触发的事件

    /*-----------第四部分-------------*/
    /*注册输入设备*/
    error = input_register_device(button_input_dev);
    if (0 != error)
    {
            printk(KERN_ERR "input_register_device error");
            gpio_free(button_GPIO_number);
            free_irq(interrupt_number, NULL);
            input_unregister_device(button_input_dev);
            return -1;
    }
    return 0;
}

驱动入口函数完成基本的初始化工作,结合代码各部分介绍如下:

  • 第9-49行:这部分和“中断实验”相同,依次执行获取设备树节点、获取GPIO、申请GPIO、获取中断号、申请中断, 需要注意的是这里中断类型为“上升和下降沿触发”。

  • 第50-58行:申请输入子系统结构体,申请得到的input_dev结构体代表了一个输入设备, 下面要根据实际的输入设备设置这个结构体。

  • 第62行:设置输入事件类型。input_dev参数很多,其中最主要的是事件类型和事件对应的code。 evbit每一位代表了一种事件类型,为1则表示支持,0表示不支持。例如我们这里要支持“按键”事件, 那么就要将EV_KEY(等于0x01)位置1。内核提供了帮助宏BIT_MASK帮助我们开启某一“事件”。

  • 第63行:设置支持的事件类型之后还要设置与之对应的“事件值”,内核文档中称为code。以按键为例,就是为按键选择键值 (在程序中通过键值区分不同的按键),input_dev->keybit参数用于选择键值,例如在驱动中有6个按键,那么就要使能6个键值, 同样input_dev->keybit每一位代表一个键值,我们可以直接设置某一位使能对应的键值, 不过内核提供了很多帮助宏或函数帮助我们设置键值(也可用于设置其他类型事件的code), 我们在程序中使用的是input_set_capability函数。:

  • 第67-75行:注册输入设备。注册成功后,输入设备被添加到输入子系统内核层,系统能够接受来自该设备的输入事件。 需要注意的是如果注册失败需要注销之前申请的资源然后退出

input_set_capability函数,原型如下:

input_set_capability函数 (内核源码/drivers/input/input.c):

void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
    switch (type) {
    case EV_KEY:
            __set_bit(code, dev->keybit);
            break;

    case EV_REL:
            __set_bit(code, dev->relbit);
            break;

    case EV_ABS:
            input_alloc_absinfo(dev);
            if (!dev->absinfo)
                    return;

            __set_bit(code, dev->absbit);
            break;

    case EV_MSC:
            __set_bit(code, dev->mscbit);
            break;

    case EV_SW:
            __set_bit(code, dev->swbit);
            break;

    case EV_LED:
            __set_bit(code, dev->ledbit);
            break;

    case EV_SND:
            __set_bit(code, dev->sndbit);
            break;

    case EV_FF:
            __set_bit(code, dev->ffbit);
            break;

    case EV_PWR:
            /* do nothing */
            break;

    default:
            pr_err("input_set_capability: unknown type %u (code %u)\n",
                   type, code);
            dump_stack();
            return;
    }

    __set_bit(type, dev->evbit);
}

参数

  • dev:指定要设置的input_dev结构体,也就是要设置的输入设备,

  • type:设置输入类型,可以看到,函数实现中根据type设置不同的input_dev结构体参数。例如type =EV_KEY, 那么设置的是input_dev->keybit,也就是键值。

  • code:不同类型的输入信号含义不同,如果是按键,则表示的是要设置的按键的键值。

返回值: 

2.2.2. 驱动出口函数

出口函数主要完成驱动退出前的清理工作,很简单,代码如下:

static void __exit button_driver_exit(void)
{
    pr_info("button_driver_exit\n");

    /*释放申请的引脚,和中断*/
    gpio_free(button_GPIO_number);
    free_irq(interrupt_number, NULL);

    /*释放输入子系统相关内容*/
    input_unregister_device(button_input_dev);
}
  • 第6-7行:释放申请的引脚和中断

  • 第10行:释放申请的输入子系统

2.2.3. 中断服务函数

中断服务函数中我们读取按键输入引脚的状态判断按键是按下还是松开。代码如下所示。

static irqreturn_t button_irq_hander(int irq, void *dev_id)
{
    int button_satus = 0;


    /*读取按键引脚的电平,根据读取得到的结果输入按键状态*/
    button_satus = gpio_get_value(button_GPIO_number);
    if(0 == button_satus)
    {

            input_report_key(button_input_dev, KEY_1, 0);
            input_sync(button_input_dev);
    }
    else
    {
            input_report_key(button_input_dev, KEY_1, 1);
            input_sync(button_input_dev);
    }

    return IRQ_HANDLED;
}
  • 第7行:读取按键对应引脚的电平。

  • 第8-18行:根据按键引脚状态向系统上报按键事件。

2.3. 测试应用程序实现

测试应用程序中读取按键键值,打印按键状态。具体代码如下所示。

struct input_event button_input_event;

int main(int argc, char *argv[])
{
    int error = -20;

    /*打开文件*/
    int fd = open("/dev/input/event1", O_RDONLY);
    if (fd < 0)
    {
        printf("open file : /dev/input/event1 error!\n");
        return -1;
    }

    printf("wait button down... \n");
    printf("wait button down... \n");

    do
    {
        /*读取按键状态*/
        error = read(fd, &button_input_event, sizeof(button_input_event));
        if (error < 0)
        {
            printf("read file error! \n");
        }
        /*判断并打印按键状态*/
        if((button_input_event.type == 1) && (button_input_event.code == 2))
        {
            if(button_input_event.value == 0)
            {
                printf("button up\n");
            }
            else if(button_input_event.value == 1)
            {
                 printf("button down\n");
            }
        }
    } while (1);

    printf("button Down !\n");

    /*关闭文件*/
    error = close(fd);
    if (error < 0)
    {
        printf("close file error! \n");
    }
    return 0;
}
  • 第1行:申请一个input_event类型的结构体变量,如我们在本章开头前所说的所有的输入设备传递的信息都会以事件的形式上报。

  • 第8行:这里的打开的文件 /dev/input/event1 是输入子系统为我们生成的输入设备设备,即我们使用的按键。

  • 第21行:读取按键信息,read函数没有读取到上报输入事件则将一直等待。

  • 第27-37行:根据获取读取到的信息判断按键的状态。

测试应用程序的内容很简单,基本是按照打开文件、读取状态、判断状态并打印状态。

2.4. 实验准备

在板卡上的部分GPIO可能会被系统占用,在使用前请根据需要修改 /boot/uEnv.txt 文件, 可注释掉某些设备树插件的加载,重启系统,释放相应的GPIO引脚。

如本节实验中,可能在鲁班猫系统中默认使能了 KEY 的设备功能, 用在了GPIO子系统。引脚被占用后,设备树可能无法再加载或驱动中无法再申请对应的资源。

方法参考如下:

broken

取消 KEY 设备树插件,以释放系统对应KEY资源,操作如下:

broken

dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-button-interrupt.dtbo

如若运行代码时出现“Device or resource busy”或者运行代码卡死等等现象, 请按上述情况检查并按上述步骤操作。

如出现 Permission denied 或类似字样,请注意用户权限,大部分操作硬件外设的功能,几乎都需要root用户权限,简单的解决方案是在执行语句前加入sudo或以root用户运行程序。

2.4.1. 编译设备树插件

将 linux_driver/input_sub_system/imx-fire-button-interrupt-overlay.dts 拷贝到 内核源码/arch/arm/boot/dts/overlays 目录下, 并修改同级目录下的Makefile,追加 imx-fire-button-interrupt.dtbo 编译选项。然后执行如下命令编译设备树插件:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig

make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs

编译成功后生成同名的设备树插件文件(imx-fire-button-interrupt.dtbo)位于 内核源码/arch/arm/boot/dts/overlays 目录下。

2.4.2. 编译驱动程序

将 linux_driver/input_sub_system 拷贝到内核源码同级目录,执行里面的MakeFile,生成input_sub_system.ko。

5|

2.4.3. 编译应用程序

将 linux_driver/input_sub_system/test_app 目录中执行里面的MakeFile,生成test_app。

编译应用程序

2.5. 下载验证

编译驱动和应用程序并拷贝到开发板,这里就不再赘述如何拷贝文件了。 在加载模块之前,先查看 /boot/uEnv.txt 文件是否加载了板子上原有的与按键相关设备树插件。 如果之前开启了按键相关的设备树插件,记得先屏蔽掉。并添加输入子系统的设备树插件。

1

在上图所示代码前添加’#’以注销掉与按键相关的设备树插件,并 reboot 重启开发板。

使用insmod命令加载驱动,如下所示:

2

此时会在“/dev/input”目录下生成设备节点文件。

3

驱动加载成功后直接运行测试应用程序命令“./test_app”.测试程序运行后等待按键按下,此时按下开发板的“KEY”按键, 终端会输出按键状态,如下所示。

4

 

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

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

相关文章

计算机毕设-基于springboot的某学院兼职平台的设计与实现(附源码+lw+ppt+开题报告)

博主介绍&#xff1a;✌多个项目实战经验、多个大型网购商城开发经验、在某机构指导学员上千名、专注于本行业领域✌ 技术范围&#xff1a;Java实战项目、Python实战项目、微信小程序/安卓实战项目、爬虫大数据实战项目、Nodejs实战项目、PHP实战项目、.NET实战项目、Golang实战…

Unity3D仿星露谷物语开发3之动画系统初探

1、目标 我们希望使用已有的资源建一个动画demo&#xff0c;以此熟悉基于已有Animator/Animation资源的使用方法。 以Tree的动画系统为例&#xff0c;资源位于&#xff1a; 2、创建流程 &#xff08;1&#xff09;创建tree空对象 上面两个都是空对象。 &#xff08;2&#…

怎么禁用 vscode 中点击 go 包名时自动打开浏览器跳转到 pkg.go.dev

本文引用怎么禁用 vscode 中点击 go 包名时自动打开浏览器跳转到 pkg.go.dev 在 vscode 设置项中配置 gopls 的 ui.navigation.importShortcut 为 Definition 即可。 "gopls": {"ui.navigation.importShortcut": "Definition" }ui.navigation.i…

Java:183 基于SSM的高校食堂系统

项目介绍 基于SSM的食堂点餐系统 角色:管理员、用户、食堂 前台用户可以实现商品浏览&#xff0c;加入购物车&#xff0c;加入收藏&#xff0c;预定&#xff0c;选座&#xff0c;个人信息管理&#xff0c;收货信息管理&#xff0c;收藏管理&#xff0c;评论功能&#xff0c;…

[COLM 2024] V-STaR: Training Verifiers for Self-Taught Reasoners

本文是对 STaR 的改进方法&#xff0c;COLM 是 Conference On Language Models&#xff0c;大模型领域新出的会议&#xff0c;在国际上很知名&#xff0c;不过目前还没有被列入 ccf list&#xff08;新会议一般不会列入&#xff09;&#xff1b;作者来自高校、微软研究院和 Goo…

端点鉴别、安全电子邮件、TLS

文章目录 端点鉴别鉴别协议ap 1.0——发送者直接发送一个报文表明身份鉴别协议ap 2.0——ap1.0 的基础上&#xff0c;接收者对报文的来源IP地址进行鉴别鉴别协议ap 3.0——使用秘密口令&#xff0c;口令为鉴别者和被鉴别者之间共享的秘密鉴别协议ap 3.1——对秘密口令进行加密&…

电脑文件夹打不开了,能打开但是会闪退,提示“找不到iUtils.dll”是什么原因?

电脑运行时常见问题解析&#xff1a;文件夹打不开、闪退及“找不到iUtils.dll”报错 在使用电脑的过程中&#xff0c;我们可能会遇到文件夹打不开、软件闪退或系统报错等问题&#xff0c;特别是提示“找不到iUtils.dll”的报错&#xff0c;更是让人困惑不已。今天我将为大家详…

MATLAB图卷积神经网络GCN处理分子数据集节点分类研究

全文链接&#xff1a;https://tecdat.cn/?p38570 本文主要探讨了如何利用图卷积网络&#xff08;GCN&#xff09;对图中的节点进行分类。介绍了相关的数据处理、模型构建、训练及测试等环节&#xff0c;通过对分子数据集的操作实践&#xff0c;展示了完整的节点分类流程&#…

计算机网络-传输层 UDP协议

学习一个网络协议&#xff0c;主要就是学习“数据格式/报文格式” UDP的特点 UDP传输的过程类似于寄信. ⽆连接: 知道对端的IP和端⼝号就直接进⾏传输, 不需要建⽴连接; 不可靠: 没有确认机制, 没有重传机制; 如果因为⽹络故障该段⽆法发到对⽅, UDP协议层也不会给应 ⽤层返回任…

[OpenGL] Transform feedback 介绍以及使用示例

一、简介 本文介绍了 OpenGL 中 Transform Feedback 方法的基本概念和代码示例。 二、Transform Feedback 介绍 1. Transform Feedback 简介 根据 OpenGL-wiki&#xff0c;Transform Feedback 是捕获由顶点处理步骤&#xff08;vertex shader 和 geometry shader&#xff0…

拆解大语言模型RLHF中的PPO

** 拆解大语言模型RLHF中的PPO ** 参考链接&#xff1a;https://zhuanlan.zhihu.com/p/645225982 为什么大多数介绍RLHF的文章&#xff0c;一讲到PPO算法的细节就戛然而止了呢&#xff1f;要么直接略过&#xff0c;要么就只扔出一个PPO的链接。然而LLM PPO跟传统的PPO还是有…

arcGIS使用笔记(无人机tif合并、导出、去除黑边、重采样)

无人机航拍建图之后&#xff0c;通过大疆智图软件可以对所飞行的区域的进行拼图&#xff0c;但是如果需要对拼好的图再次合并&#xff0c;则需要利用到arcGIS软件。下面介绍arcGIS软件在这个过程中常用的操作。 1.导入tif文件并显示的方法&#xff1a;点击“”图标进行导入操作…

利用代理IP爬取Zillow房产数据用于数据分析

引言 最近数据分析的热度在编程社区不断攀升&#xff0c;有很多小伙伴都开始学习或从事数据采集相关的工作。然而&#xff0c;网站数据已经成为网站的核心资产&#xff0c;许多网站都会设置一系列很复杂的防范措施&#xff0c;阻止外部人员随意采集其数据。为了解决这个问题&a…

免费送源码:Java+B/S+MySQL 多元化智能选课系统的设计与实现 计算机毕业设计原创定制

摘 要 多元化智能选课系统使用Java语言的Springboot框架&#xff0c;采用MVVM模式进行开发&#xff0c;数据方面主要采用的是微软的Mysql关系型数据库来作为数据存储媒介&#xff0c;配合前台技术完成系统的开发。 论文主要论述了如何使用JAVA语言开发一个多元化智能选课系统&…

(九)机器学习 - 多项式回归

多项式回归&#xff08;Polynomial Regression&#xff09;是一种回归分析方法&#xff0c;它将自变量 xx 和因变量 yy 之间的关系建模为 nn 次多项式。多项式回归的目的是找到一个 nn 次多项式函数&#xff0c;使得这个函数能够最好地拟合给定的数据点。 多项式回归的数学表达…

XX服务器上的npm不知道咋突然坏了

收到同事的V&#xff0c;说是&#xff1a;182上的npm不知道咋突然坏了&#xff0c;查到这里了&#xff0c;不敢动了。 咱一定要抓重点&#xff1a;突然坏了。这里的突然肯定不是瞬间&#xff08;大概率是上次可用&#xff0c;这次不可用&#xff0c;中间间隔了多长时间&#x…

Vizcom:AI驱动的草图到3D设计革命

Vizcom是一家领先的AI技术公司,专注于为工业设计师提供工具,将手绘草图快速转化为可制造的3D模型,从而加速产品迭代和创新。 公司背景与愿景 成立于2021年的Vizcom由前Nvidia工业设计师Jordan Taylor创立。Taylor凭借其深厚的创意设计背景和技术敏锐度,看到了生成对抗网络…

html自带的input年月日(date) /时间(datetime-local)/星期(week)/月份(month)/时间(time)控件

年月日期控件 type"date" <input type"date" id"StartDate" valueDateTime.Now.ToString("yyyy-MM-dd") /> //设置值 $("#StartDate").val("2024-12-12"); //获取值 var StartDate$("#StartDate&quo…

【51单片机】独立按键快速上手

51单片机独立按键是单片机控制系统中常用的一种输入方式&#xff0c;它相当于一种电子开关&#xff0c;按下时开关接通&#xff0c;松开时开关断开。 开关功能‌&#xff1a;独立按键内部通常包含一个有弹性的金属片&#xff0c;当按键被按下时&#xff0c;金属片与触点接触&a…

SpringCloud和Nacos的基础知识和使用

1.什么是SpringCloud ​ 什么是微服务&#xff1f; ​ 假如我们需要搭建一个网上购物系统&#xff0c;那么我们需要哪些功能呢&#xff1f;商品中心、订单中心和客户中心等。 ​ 当业务功能较少时&#xff0c;我们可以把这些功能塞到一个SpringBoot项目中来进行管理。但是随…