uvc摄像头驱动uvc设备的注册分析

news2024/11/15 20:54:26

uvc摄像头驱动uvc设备的注册分析


文章目录

  • uvc摄像头驱动uvc设备的注册分析
  • uvc_init
  • uvc_probe
  • uvc_register_video
    • uvc_register_chains
    • uvc_register_terms
    • uvc_register_video
  • uvc_ioctl_ops
  • uvc_fops


在这里插入图片描述

uvc_init

/driver/media/usb/uvc/uvc_driver.c

/*
 * UVC 驱动结构体
 */
struct uvc_driver uvc_driver = {
    .driver = {
        .name        = "uvcvideo", // 驱动名
        .probe        = uvc_probe, // 探测函数
        .disconnect    = uvc_disconnect, // 断开连接函数
        .suspend    = uvc_suspend, // 挂起函数
        .resume        = uvc_resume, // 恢复函数
        .reset_resume    = uvc_reset_resume, // 重置恢复函数
        .id_table    = uvc_ids, // 设备 ID 表
        .supports_autosuspend = 1, // 支持自动挂起
    },
};

该结构体的作用是初始化UVC(USB Video Class)驱动。主要包括以下步骤:

  1. 调用uvc_debugfs_init()函数初始化调试文件系统,用于在调试过程中提供驱动相关的调试信息。
  2. 调用usb_register()函数注册USB设备,将UVC驱动与USB设备进行关联。此处使用的是uvc_driver结构体中定义的USB驱动。
  3. 如果USB设备注册失败(ret小于0),则调用uvc_debugfs_cleanup()函数清理调试文件系统,释放相关资源,并返回错误码。
  4. 如果USB设备注册成功,使用printk()函数打印驱动的描述信息(DRIVER_DESC)和版本号(DRIVER_VERSION)。
  5. 返回0,表示驱动初始化成功。
    总体来说,该函数用于初始化UVC驱动,并将其注册到USB子系统中,以便与相应的USB设备进行匹配和通信。同时,它还处理了调试文件系统的初始化和清理,以及打印驱动信息的功能。
/*
 * uvc_init - UVC 驱动初始化函数
 */
static int __init uvc_init(void)
{
    int ret;

    /*
     * 初始化调试文件系统
     */
    uvc_debugfs_init();

    /*
     * 注册 USB 设备
     */
    ret = usb_register(&uvc_driver.driver);
    if (ret < 0) {
        /*
         * 注册失败,清理调试文件系统
         */
        uvc_debugfs_cleanup();
        return ret;
    }

    /*
     * 打印驱动信息
     */
    printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
    return 0;
}

uvc_init->usb_register->

pr_info("%s: registered new interface driver %s\n",
            usbcore_name, new_driver->name);

在这里插入图片描述

接入uvc驱动支持的摄像头,并匹配成功之后调用uvc_probe函数

uvc_probe

/driver/media/usb/uvc/uvc_driver.c
该函数是UVC(USB Video Class)驱动中的设备探测函数,用于处理USB设备与UVC驱动的匹配和初始化工作。一般情况下,它会在USB设备与UVC驱动匹配成功时被调用。
作用:

  1. 确定USB设备是否与当前的UVC驱动匹配,通过检查设备的VID(Vendor ID)和PID(Product ID)等信息。
  2. 分配并初始化UVC设备的数据结构,如uvc_device、uvc_streaming等。
  3. 配置和注册UVC设备,包括分配和初始化视频流的端点、请求USB设备资源等。
  4. 注册视频设备,创建V4L2(Video4Linux2)设备节点,以便用户空间可以访问和操作视频流。
  5. 启动视频流的传输和捕获,包括设置视频格式、帧率等参数,开启视频流传输通道。
  6. 设置V4L2设备的相关属性和回调函数,以响应用户空间的操作和请求。
    通常,当插入支持UVC协议的USB摄像头或视频设备时,USB子系统会检测到设备并调用uvc_probe()函数,以便与UVC驱动进行匹配和初始化。
static int uvc_probe(struct usb_interface *intf,
             const struct usb_device_id *id)
{
    // 获取 USB 设备
    struct usb_device *udev = interface_to_usbdev(intf);
    // 定义 UVC 设备
    struct uvc_device *dev;
    int ret;

    // 如果 idVendor 和 idProduct 都存在,则打印已知 UVC 设备的信息
    if (id->idVendor && id->idProduct)
        uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
                "(%04x:%04x)\n", udev->devpath, id->idVendor,
                id->idProduct);
    // 否则打印通用 UVC 设备的信息
    else
        uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
                udev->devpath);

    // 为设备分配内存并初始化
    if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
        return -ENOMEM;

// 初始化设备的各个链表
    INIT_LIST_HEAD(&dev->entities);
    INIT_LIST_HEAD(&dev->chains);
    INIT_LIST_HEAD(&dev->streams);
    atomic_set(&dev->nstreams, 0);
    atomic_set(&dev->nmappings, 0);
    mutex_init(&dev->lock);

    // 获取 USB 设备
    dev->udev = usb_get_dev(udev);
    dev->intf = usb_get_intf(intf);
    dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
    dev->quirks = (uvc_quirks_param == -1)
            ? id->driver_info : uvc_quirks_param;

    // 如果 USB 设备有产品名称,则使用该名称,否则使用默认名称
    if (udev->product != NULL)
        strlcpy(dev->name, udev->product, sizeof dev->name);
    else
        snprintf(dev->name, sizeof dev->name,
            "UVC Camera (%04x:%04x)",
            le16_to_cpu(udev->descriptor.idVendor),
            le16_to_cpu(udev->descriptor.idProduct));

    // 解析 Video Class 控制描述符
    if (uvc_parse_control(dev) < 0) {
        uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
            "descriptors.\n");
        goto error;
    }

    // 打印 UVC 设备信息
    uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
        dev->uvc_version >> 8, dev->uvc_version & 0xff,
        udev->product ? udev->product : "<unnamed>",
        le16_to_cpu(udev->descriptor.idVendor),
        le16_to_cpu(udev->descriptor.idProduct));

if (dev->quirks != id->driver_info) {
        // 如果设备 quirks 不等于驱动程序信息,则打印信息
        uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
            "parameter for testing purpose.\n", dev->quirks);
        // 打印信息
        uvc_printk(KERN_INFO, "Please report required quirks to the "
            "linux-uvc-devel mailing list.\n");
    }

    /* Register the media and V4L2 devices. */
#ifdef CONFIG_MEDIA_CONTROLLER
    // 设置 media 设备的信息
    dev->mdev.dev = &intf->dev;
    strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
    if (udev->serial)
        strlcpy(dev->mdev.serial, udev->serial,
            sizeof(dev->mdev.serial));
    strcpy(dev->mdev.bus_info, udev->devpath);
    dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
    dev->mdev.driver_version = LINUX_VERSION_CODE;
    // 注册 media 设备
    if (media_device_register(&dev->mdev) < 0)
        goto error;

    // 设置 v4l2 设备的信息
    dev->vdev.mdev = &dev->mdev;
#endif
// 注册 v4l2 设备
    if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
        goto error;

    /* Initialize controls. */
    // 初始化控制器
    if (uvc_ctrl_init_device(dev) < 0)
        goto error;

    /* Scan the device for video chains. */
    // 扫描设备的视频链
    if (uvc_scan_device(dev) < 0)
        goto error;

    /* Register video device nodes. */
    // 注册视频设备节点
    if (uvc_register_chains(dev) < 0)
        goto error;

    /* Save our data pointer in the interface data. */
    // 在接口数据中保存数据指针
    usb_set_intfdata(intf, dev);

    /* Initialize the interrupt URB. */
    // 初始化中断 URB
    if ((ret = uvc_status_init(dev)) < 0) {
        uvc_printk(KERN_INFO, "Unable to initialize the status "
            "endpoint (%d), status interrupt will not be "
            "supported.\n", ret);
    }

    // 打印 UVC 设备初始化信息
    uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
    // 启用 USB 自动挂起
    usb_enable_autosuspend(udev);
    return 0;

error:
    // 注销视频设备
    uvc_unregister_video(dev);
    return -ENODEV;
}

该函数是UVC(USB Video Class)驱动中的设备探测函数,用于初始化和注册UVC设备,并进行设备的配置和初始化工作。
概括如下:

  1. 获取USB设备的相关信息,并创建并初始化UVC设备结构体。
  2. 初始化设备的链表、计数器和互斥锁等。
  3. 解析USB设备的描述符,包括Video Class控制描述符。
  4. 打印设备信息和版本号。
  5. 注册媒体设备(如果启用了CONFIG_MEDIA_CONTROLLER)和V4L2设备。
  6. 初始化控制器和控制设备。
  7. 扫描设备以获取视频链。
  8. 注册视频设备节点。
  9. 在接口数据中保存设备指针。
  10. 初始化中断URB(USB Request Block)。
  11. 打印设备初始化信息。
  12. 启用USB自动挂起功能。
  13. 返回初始化成功或失败的状态。
    通常情况下,当插入支持UVC协议的USB摄像头或视频设备时,USB子系统会调用uvc_probe()函数,以便进行UVC设备的初始化、配置和注册。
// 打印 UVC 设备信息
uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
    dev->uvc_version >> 8, dev->uvc_version & 0xff,
    udev->product ? udev->product : "<unnamed>",
    le16_to_cpu(udev->descriptor.idVendor),
    le16_to_cpu(udev->descriptor.idProduct));

uvc_register_video

uvc_probe->uvc_register_chains->uvc_register_terms->uvc_register_video

uvc_register_chains

该函数用于注册 UVC 设备中的所有视频链和相关的终端,如果支持 Media Controller,则还会注册相应的实体。这样可以将 UVC 设备在系统中进行识别和管理,并建立视频链与终端之间的关联关系。

/*
 * 注册所有的视频链
 */
static int uvc_register_chains(struct uvc_device *dev)
{
    struct uvc_video_chain *chain;
    int ret;

    // 遍历所有的视频链
    list_for_each_entry(chain, &dev->chains, list) {
        // 注册视频链中的所有终端
        ret = uvc_register_terms(dev, chain);
        if (ret < 0)
            return ret;

#ifdef CONFIG_MEDIA_CONTROLLER
        // 如果支持 media controller,则注册实体
        ret = uvc_mc_register_entities(chain);
        if (ret < 0) {
            uvc_printk(KERN_INFO, "Failed to register entites "
                "(%d).\n", ret);
        }
#endif
    }

    return 0;
}
  1. 遍历所有的视频链。
  2. 对于每个视频链,调用 uvc_register_terms() 函数注册视频链中的所有终端。
  3. 如果系统配置支持 Media Controller(CONFIG_MEDIA_CONTROLLER 宏定义),则调用 uvc_mc_register_entities() 函数注册视频链的实体。
  4. 返回成功。

uvc_register_terms

该函数的作用是将视频链中的流式传输终端与相应的流式传输结构体关联起来,并注册视频设备。通过这样的关联,可以在 UVC 设备中建立起视频链、终端和流式传输之间的关联关系,以便在数据传输和控制过程中进行正确的处理和管理。

static int uvc_register_terms(struct uvc_device *dev,
    struct uvc_video_chain *chain)
{
    struct uvc_streaming *stream;
    struct uvc_entity *term;
    int ret;

    // 遍历视频链中的所有实体
    list_for_each_entry(term, &chain->entities, chain) {
        // 如果实体不是流式传输终端,则跳过
        if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
            continue;

        // 根据终端 ID 获取流式传输结构体
        stream = uvc_stream_by_id(dev, term->id);
        if (stream == NULL) {
            uvc_printk(KERN_INFO, "No streaming interface found "
                   "for terminal %u.", term->id);
            continue;
        }

        // 将流式传输结构体与视频链关联
        stream->chain = chain;
        // 注册视频设备
        ret = uvc_register_video(dev, stream);
        if (ret < 0)
            return ret;

        // 将视频设备与终端关联
        term->vdev = &stream->vdev;
    }

    return 0;
}
  1. 遍历给定视频链 chain 中的所有实体。
  2. 对于每个实体,如果它不是流式传输终端(UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING),则跳过。
  3. 根据终端的 ID,在设备 dev 中查找对应的流式传输结构体 stream。
  4. 如果找到了流式传输结构体,将其与视频链关联(stream->chain = chain)。
  5. 调用 uvc_register_video() 函数注册视频设备。
  6. 将视频设备与终端关联(term->vdev = &stream->vdev)。
  7. 返回成功。

uvc_register_video

该函数负责初始化和注册视频设备,使其能够在V4L2框架中正常工作,并与特定的流式传输相关联。

static int uvc_register_video(struct uvc_device *dev,
        struct uvc_streaming *stream)
{
    // 初始化视频缓冲区队列
    int ret = uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
    if (ret)
        return ret;

    // 使用默认的流式传输参数初始化流式传输接口
    ret = uvc_video_init(stream);
    if (ret < 0) {
        uvc_printk(KERN_ERR, "Failed to initialize the device "
            "(%d).\n", ret);
        return ret;
    }

    // 初始化调试文件系统
    uvc_debugfs_init_stream(stream);

    // 在 V4L 中注册设备

    // 我们已经持有对 dev->udev 的引用。在引用被释放之前,视频设备将被注销,因此我们不需要获取另一个引用。
    struct video_device *vdev = &stream->vdev;
    vdev->v4l2_dev = &dev->vdev;
    vdev->fops = &uvc_fops;
    vdev->ioctl_ops = &uvc_ioctl_ops;
    vdev->release = uvc_release;
    vdev->prio = &stream->chain->prio;
    if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
        vdev->vfl_dir = VFL_DIR_TX;
    strlcpy(vdev->name, dev->name, sizeof vdev->name);

    // 在调用 video_register_device 之前设置驱动程序数据,否则 uvc_v4l2_open 可能会与我们竞争。
    video_set_drvdata(vdev, stream);

    ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
    if (ret < 0) {
        uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",
               ret);
        return ret;
    }

    if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
        stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE;
    else
        stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT;

    atomic_inc(&dev->nstreams);
    return 0;
}

这个函数在Linux内核uvc_driver驱动中的作用是注册视频设备。它执行以下操作:

  1. 初始化视频缓冲区队列,使用指定的流类型和参数初始化视频队列。
  2. 使用默认的流式传输参数初始化流式传输接口,包括分配和配置相关的数据结构和资源。
  3. 初始化调试文件系统,用于调试和监控该视频流。
  4. 在V4L2框架中注册视频设备。它设置视频设备的各种属性,如设备名称、文件操作函数、IO控制操作函数等。
  5. 设置视频设备的驱动程序数据,以便将其与流式传输相关联。
  6. 调用video_register_device函数,在V4L2框架中注册视频设备。
  7. 更新视频链的功能标志,根据流的类型设置相应的功能标志。
  8. 增加视频设备数量的计数器。

uvc_ioctl_ops

// 定义V4L2的ioctl操作函数集合
const struct v4l2_ioctl_ops uvc_ioctl_ops = {
    .vidioc_querycap = uvc_ioctl_querycap, // 查询设备的能力
    .vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap, // 枚举视频捕获格式
    .vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt_vid_out, // 枚举视频输出格式
    .vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap, // 获取视频捕获格式
    .vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt_vid_out, // 获取视频输出格式
    .vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap, // 设置视频捕获格式
    .vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out, // 设置视频输出格式
    .vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap, // 尝试设置视频捕获格式
    .vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt_vid_out, // 尝试设置视频输出格式
    .vidioc_reqbufs = uvc_ioctl_reqbufs, // 请求缓冲区
    .vidioc_querybuf = uvc_ioctl_querybuf, // 查询缓冲区
    .vidioc_qbuf = uvc_ioctl_qbuf, // 将缓冲区放入队列
    .vidioc_dqbuf = uvc_ioctl_dqbuf, // 从队列中取出缓冲区
    .vidioc_create_bufs = uvc_ioctl_create_bufs, // 创建缓冲区
    .vidioc_streamon = uvc_ioctl_streamon, // 开始视频流
    .vidioc_streamoff = uvc_ioctl_streamoff, // 停止视频流
    .vidioc_enum_input = uvc_ioctl_enum_input, // 枚举输入
    .vidioc_g_input = uvc_ioctl_g_input, // 获取输入
    .vidioc_s_input = uvc_ioctl_s_input, // 设置输入
    .vidioc_queryctrl = uvc_ioctl_queryctrl, // 查询控制器
    .vidioc_query_ext_ctrl = uvc_ioctl_query_ext_ctrl, // 查询扩展控制器
    .vidioc_g_ctrl = uvc_ioctl_g_ctrl, // 获取控制器
    .vidioc_s_ctrl = uvc_ioctl_s_ctrl, // 设置控制器
    .vidioc_g_ext_ctrls = uvc_ioctl_g_ext_ctrls, // 获取扩展控制器
    .vidioc_s_ext_ctrls = uvc_ioctl_s_ext_ctrls, // 设置扩展控制器
    .vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls, // 尝试设置扩展控制器
    .vidioc_querymenu = uvc_ioctl_querymenu, // 查询菜单
    .vidioc_g_selection = uvc_ioctl_g_selection, // 获取选择
    .vidioc_g_parm = uvc_ioctl_g_parm, // 获取参数
    .vidioc_s_parm = uvc_ioctl_s_parm, // 设置参数
    .vidioc_enum_framesizes = uvc_ioctl_enum_framesizes, // 枚举帧大小
    .vidioc_enum_frameintervals = uvc_ioctl_enum_frameintervals, // 枚举帧间隔
    .vidioc_subscribe_event = uvc_ioctl_subscribe_event, // 订阅事件
    .vidioc_unsubscribe_event = v4l2_event_unsubscribe, // 取消订阅事件
    .vidioc_default = uvc_ioctl_default, // 默认操作
};

结构体 const struct v4l2_ioctl_ops uvc_ioctl_ops 定义了与视频设备 IOCTL 请求相关的函数指针,它的作用是为 UVC(USB Video Class)驱动程序提供对视频设备的控制接口。
该结构体包含了以下函数指针成员:
vidioc_querycap:用于查询设备的能力和属性的回调函数。
vidioc_enum_fmt_vid_cap:用于枚举支持的视频捕获格式的回调函数。
vidioc_g_fmt_vid_cap:用于获取当前视频捕获格式的回调函数。
vidioc_s_fmt_vid_cap:用于设置视频捕获格式的回调函数。
vidioc_reqbufs:用于请求视频缓冲区的回调函数。
vidioc_querybuf:用于查询视频缓冲区信息的回调函数。
vidioc_qbuf:用于将已填充数据的视频缓冲区排队到驱动程序的回调函数。
vidioc_dqbuf:用于从驱动程序中取出已处理数据的视频缓冲区的回调函数。
vidioc_streamon:用于启动视频数据流的回调函数。
vidioc_streamoff:用于停止视频数据流的回调函数。
这些回调函数实现了对视频设备的控制和数据流操作,包括查询设备属性、设置和获取视频格式、请求和查询视频缓冲区信息、数据缓冲的排队和取出,以及启动和停止视频数据流等。通过指定这些函数指针,驱动程序可以处理相应的 IOCTL 请求,与用户空间的应用程序进行交互,并控制视频设备的行为。

uvc_fops

const struct v4l2_file_operations uvc_fops = {
    .owner      = THIS_MODULE, // 指向拥有这个结构体的模块的指针
    .open       = uvc_v4l2_open, // 打开设备文件时调用的函数
    .release    = uvc_v4l2_release, // 关闭设备文件时调用的函数
    .unlocked_ioctl = video_ioctl2, // 处理ioctl命令的函数
#ifdef CONFIG_COMPAT
    .compat_ioctl32 = uvc_v4l2_compat_ioctl32, // 处理32位ioctl命令的函数
#endif
    .read       = uvc_v4l2_read, // 读取设备文件时调用的函数
    .mmap       = uvc_v4l2_mmap, // 映射设备文件到用户空间时调用的函数
    .poll       = uvc_v4l2_poll, // 等待设备文件可读或可写时调用的函数
#ifndef CONFIG_MMU
    .get_unmapped_area = uvc_v4l2_get_unmapped_area, // 获取未映射的内存区域时调用的函数
#endif
};

结构体 const struct v4l2_file_operations uvc_fops 定义了与视频设备文件操作相关的函数指针,它的作用是为 UVC(USB Video Class)驱动程序提供视频设备文件的操作接口。
该结构体包含了以下函数指针成员:
open:用于打开视频设备文件的回调函数。
release:用于关闭视频设备文件的回调函数。
unlocked_ioctl:用于处理视频设备文件的 IOCTL 请求的回调函数。
poll:用于轮询视频设备文件的回调函数,检查文件是否可读或可写。
mmap:用于实现内存映射的回调函数,将视频设备的内存映射到用户空间。
read:用于从视频设备文件中读取数据的回调函数。
write:用于向视频设备文件中写入数据的回调函数。
这些回调函数实现了对视频设备文件的常见操作,例如打开、关闭、读取、写入、控制和轮询等。通过指定这些函数指针,驱动程序可以与用户空间的应用程序进行交互,并实现相应的操作逻辑。

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

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

相关文章

每日学术速递5.19

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.On the Hidden Mystery of OCR in Large Multimodal Models 标题&#xff1a;论大型多模态模型中 OCR 的隐藏奥秘 作者&#xff1a;Yuliang Liu, Zhang Li, Hongliang Li, Wenwen…

计算机图形学-GAMES101-11

显式几何的表示方法 Point Cloud 使用一系列点表示物体的表面。理论上可以表达空间中任何物体。如物体扫描会得到空间中一系列点&#xff0c;但在建模时我们要提取出大量多边形的面。点云对密度要求很高&#xff0c;因此不常使用。 Polygon Mesh 使用三角形表示物体。涉及三…

备份树莓派SD卡 — 保姆级教学

在我们树莓派项目开发的过程中&#xff0c;经常遇到以下问题&#xff1a; 1.自己辛辛苦苦开发出来的项目&#xff0c;害怕内存卡损坏&#xff0c;系统被破坏掉&#xff0c;想做一个备份。 2.自己的树莓派内存卡内存不够了&#xff0c;想将原来卡上的内容放在新的大容量内存卡…

Arm微架构分析系列3——Arm的X计划

1. 引言 前文介绍了Arm公司近几年在移动处理器市场推出的Cortex-A系列处理器。Cortex-A系列处理器每年迭代&#xff0c;性能和能效不断提升&#xff0c;是一款非常成功的产品。但是&#xff0c;Arm并不满足于Cortex-A系列每年的架构小幅度升级&#xff0c;又推出了X计划&#x…

esp32CAM环境安装教程---串口驱动安装

前言 &#xff08;1&#xff09;本人安装好arduino 的ESP32环境之后&#xff0c; 发现一直下载不进去程序。一直说Cannot configure port, something went wrong. Original message: PermissionError。 &#xff08;2&#xff09;查阅了很多资料&#xff0c;用了各种办法&#…

怎么消除文法的左递归性

除文法的左递归性可以采用以下方法&#xff1a; 直接左递归转换为间接左递归消除间接左递归 举例说明&#xff1a; 直接左递归转换为间接左递归 原文法&#xff1a;A → Aα | β 转换后的文法&#xff1a;A → βA A → αA | ε 例如&#xff1a;S → Sabc | ε 转换后…

4. QT中的事件函数 --- 鼠标事件、键盘事件、定时器事件、绘图事件

1. 说明 在QT的控件或者窗口当中&#xff0c;如果对于当前鼠标或者键盘的功能需要自己定义&#xff0c;可以重写父类当中对应虚函数&#xff0c;主要包括以下几个&#xff1a; //键盘按键按下 virtual void keyPressEvent(QKeyEvent *event); //键盘按键抬起 virtual void ke…

11.1网络编程

多线程 一、基础知识概念相关API二、任务创建一个简单的本地客户端迭代服务器select系统调用并发服务器数据报三、总结四、问题一、基础知识 概念 网络编程中客户端和服务器指的是进程,而不是常提到的机器或者主机。注意三个概念:请求、响应、事务。 网络编程中客户端-服务器…

面向对象的三大特性之继承(C++)

文章目录 继承的概念和定义概念定义定义格式继承关系和访问限定符继承基类成员访问方式的变化 基类和派生类对象赋值转换继承中的作用域派生类的默认成员函数继承与友元继承与静态成员菱形继承与虚拟继承菱形继承虚拟继承 继承的总结与反思继承和组合 继承的概念和定义 概念 继…

微信小程序 nodejs+vue+uniapp付费自习室图书馆教室座位系统-

系统分为用户和管理员角色 管理员的主要功能有&#xff1a; 1.管理员输入账户登陆后台 2.个人中心&#xff1a;管理员修改密码和账户信息 3.用户管理&#xff1a;对注册的用户信息进行添加&#xff0c;删除&#xff0c;修改&#xff0c;查询 4.自习室管理&#xff1a;对系统的自…

由浅入深Netty协议设计与解析

目录 1 为什么需要协议&#xff1f;2 redis 协议举例3 http 协议举例4 自定义协议要素4.1 编解码器4.2 什么时候可以加 Sharable 1 为什么需要协议&#xff1f; TCP/IP 中消息传输基于流的方式&#xff0c;没有边界。 协议的目的就是划定消息的边界&#xff0c;制定通信双方要…

每日学术速递5.18

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Make-A-Protagonist: Generic Video Editing with An Ensemble of Experts 标题&#xff1a;Make-A-Protagonist&#xff1a;与专家合奏的通用视频编辑 作者&#xff1a;Yuyang Z…

云端一体助力体验升级和业务创新

随着音视频和AI技术的发展&#xff0c;在满足用户基础体验和需求情况下&#xff0c;更极致的用户体验和更丰富的互动玩法&#xff0c;成为各个平台打造核心竞争力的关键。LiveVideoStackCon 2022 北京站邀请到火山引擎视频云华南区业务负责人——张培垒&#xff0c;基于节跳动音…

虚幻引擎4利用粒子系统实现物体轨迹描绘

虚幻引擎4利用粒子系统实现物体轨迹描绘 目录 虚幻引擎4利用粒子系统实现物体轨迹描绘前言粒子系统利用粒子系统实现物体轨迹描绘创建粒子系统将粒子系统的产生位置绑定到运动物体上 小结 前言 由于在物体运动时&#xff0c;想要观察其总的运动轨迹&#xff0c;以便对其控制做…

Java实现天气预报功能

如果要实现类似百度天气、手机App这样的天气预报功能该如何实现&#xff1f;首先想到的是百度... 背景&#xff1a; 最近公司做了一个项目&#xff0c;天气预报的功能也做上去了&#xff0c;不仅有实时天气、未来7天预报的功能、还有气象预警的功能。 天气包括基本天气、白天夜…

【K8s】什么是helm?helm的常用指令

文章目录 一、Helm介绍1、背景2、介绍3、核心概念4、chart的基本结构5、helm官网 二、部署Helm1、安装helm客户端2、安装Tiller 三、常用指令1、仓库相关 helm repo2、chart相关3、release相关 四、入门案例1、构建第一个chart2、将chart包发布到Repository3、在 Kubernetes 中…

Nacos之服务注册中心

1.Nacos之服务提供者注册 官方文档 1.1.前期工作 1.1.1.新建Module - api-commons POM <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSc…

区块链商业模式

1. 引言 web2 vs web3&#xff1a; 基于区块链的商业模式有&#xff1a; 1&#xff09;Token Economy-Utility Token商业模式2&#xff09;Blockchain As A Service&#xff08;Baas&#xff09;商业模式3&#xff09;Blockchain-Based Software Products商业模式4&#xf…

【C++修炼之路】30.可变参数模板包装器

每一个不曾起舞的日子都是对生命的辜负 C11之可变参数模板&&包装器 前言一.可变参数模板的首次登场二.参数包展开2.1 递归函数方式展开参数包2.2 逗号表达式展开参数包 三.容器的emplace方法四.包装器4.1 什么是function4.2 function包装器的作用4.3 function的实际用途…

使用Redis实现短信验证码登录功能

一、概述 目前微信小程序或网站的登录方式大部分采取了微信扫码或短信验证码等方式&#xff0c;为什么短信验证码登录方式会受到互联网公司的青睐&#xff0c;因为其确实有许多好处&#xff1a; 方便快捷&#xff1a;用户无需记忆复杂的用户名和密码&#xff0c;只需通过短信…