Linux USB 驱动开发(五)—— USB驱动程序开发过程简单总结

2023-05-16

       设备驱动程序是操作系统内核和机器硬件之间的接口,由一组函数和一些私有数据组成,是应用程序和硬件设备之间的桥梁。在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。

      设备驱动程序是内核的一部分,主要完成以下功能:对设备的初始化和释放把数据从内核传送到硬件设备和从硬件设备读取数据读取应用程序数据传送给设备文件和回送应用程序请求的数据检测和处理硬件设备出现的错误


一、 Linux USB子系统分析

        在Linux系统中,USB主机驱动程序由3部分组成:USB主机控制器驱动(HCD)USB核心驱动(USBD)不同种类的USB设备类驱动,如下所示。其中HCD和USBD被称为协议软件或者协议栈,这两部分共同处理与协议相关的操作。

       USB设备类驱动可以包含多个,不同的功能接口对应不同的驱动程序,它们不直接与USB设备硬件打交道,而是通过协议软件的抽象处理来完成与设备的不同功能接口之间的通信

       在Linux USB子系统中,HCD是直接和硬件进行交互的软件模块是USB协议栈的最底层部分是USB主机控制器硬件和数据传输的一种抽象

      HCD向上仅对USB总线驱动程序服务,HCD提供了一个软件接口,即HCDI,使得各种USB主机控制器的硬件特性都被软件化,并受USB总线驱动程序的调用和管理。HCD向下则直接管理和检测主控制器硬件的各种行为。HCD提供的功能主要有:主机控制器硬件初始化;为USBD层提供相应的接口函数;提供根HUB(ROOT HUB)设备配置、控制功能;完成4种类型的数据传输等。

      USBD部分是整个USB主机驱动的核心,主要实现的功能有:USB总线管理;USB总线设备管理、USB总线带宽管理、USB的4种类型数据传输、USB HUB驱动、为USB设备驱动提供相关接口、提供应用程序访问USB系统的文件接口等。其中USB HUB作为一类特殊的USB设备,其驱动程序被包含在USBD层。

     在嵌入式Linux系统中,已经包含HCD模块和USB核心驱动USBD,不需要用户重新编写,用户仅仅需要完成USB设备类驱动即可。


二、Linux系统中USB子系统的主要数据结构

        Linux系统中,USBD通过定义一组宏、数据结构和函数来抽象出所有硬件或是设备具有依赖关系的部分。

USBD中主要有四个数据结构,分别是:

1.usb_device保存一个USB设备的信息,包括设备地址,设备描述符,配置描述符等。

2.usb_bus保存一个USB总线系统的信息,包括总线上设备地址信息,根集线器,带宽使用情况等。一个USB总线系统至少有一个主机控制器一个根集线器,Linux系统支持多USB总线系统。

3.usb_driver保存客户驱动信息,包括驱动名称,以及驱动提供给USB内核使用的函数指针等。

4.URB(Universal Request Block)是进行USB通信的数据结构,USBD通过URB在USB设备类驱动和USBD、USBD和HCD间进行数据传输。


三、Linux系统中USB设备的加载与卸载

       当把一个USB设备插入到一个USB HUB的某个端口时,集中器就会检测到设备的接入,从而在下一次受到主机通过中断交互查询时就会向其报告。集中器的端口在没有设备接入时都处于关闭状态,插入设备之后也不会自动打开,必须由主机通过控制交互发出命令予以打开。所以,在得到集中器的报告之后,主机的USB驱动程序就会为新插入的设备调度若干个控制交互,并向集中器发出打开这个端口的命令,这样新插入的设备就会出现在USB总线上了,并为该设备分配唯一的地址

       HUB驱动程序调用函数usb_connect(struct usb_device *dev)usb_new_device(struct usb_device *dev)解析设备的各种描述符信息,分配资源,并与相应的设备驱动程序建立联系。

函数usb_new_device主要完成以下工作:

1.调用usb_set_address把新分配的设备地址传送给设备。

2.调用usb_get_descriptor获得设备的设备描述符,得到设备端点的包的最大长度,接下来的控制传输按这个数据包最大长度进行。

3.调用usb_get_configuration得到设备的所有配置描述符、接口描述符和端点描述符信息。

4.调用usb_set_configuration激活当前的配置作为默认工作配置。

5.在目录“proc/bus/usb”中为设备创建节点。

6.在USB子系统中,通过函数usb_find_driversusb_find_interface_driver为设备的每一个接口寻找相应的驱动程序,驱动程序对接口进行配置并为它们分配所需的资源。当每个接口被成功驱动后,此设备就能正常工作了。

      设备拔下时,与之相联的集线器首先检测到设备的拔下信号,通过中断传输将信息传送给集线器的驱动,集线器的驱动先验证设备是否被拔下,如果是则调用usb_disconnect(struct usb_device **pdev)进行处理。设备断开后,USB系统找到设备当前活动配置的每个接口的驱动程序,调用它们提供的disconnect接口函数,中断它们与各个接口的数据传输操作,释放它们为每个接口分配的资源。如果此设备是集线器,则递归调用usb_disconnect来处理它的子设备,释放设备地址,通过usbdevfs_remove_device函数释放给设备创建的文件节点,通过usb_free_dev释放USBD给设备分配的资源。


四、编写USB驱动程序步骤

1、所有usb驱动都必须创建主要结构体struct usb_driver

struct usb_driver

->struct module *owner

   (有他可正确对该驱动程序引用计数,应为THIS_MODULE)

->const char *name

   (驱动名字,运行时可在查看 /sys/bus/usb/drivers/)

->const struct usb_device_id *id_table

   (包含该驱动可支持的所有不同类型的驱动设备,没添探测回调函数不会被调用)

->int (*probe)(struct usb_interface *intf,const struct usb_device_id *id)

   (usb驱动探测函数,确认后struct usb_interface 应恰当初始化,然后返0,如果出错则返负值)

->void(*disconnect)(struct usb_interface *intf)

   (当struct usb_interface 被从系统中移除或驱动正从usb核心中卸载时,usb核心将调用此函数)

代码实例:

 static struct usb_driver skel_driver={
    .owner = THIS_MODULE,
    .name = "skeleton",
    .id_table = skel_table,
    .probe = skel_probe,
    .disconnect = skel_disconnect,
};

2、usb_register()注册将struct usb_driver 注册到usb核心,传统是在usb驱动程序模块初始化代码中完成该工作的

static int __init usb_skel_init(void)
{
       ... 
       usb_register(&skel_driver);
       ...
}

3、struct usb_device_id usb核心用该表判断哪个设备该使用哪个驱动程序,热插拔脚本使用它来确定当一个特定的设备插入到系统时该自动装载哪个驱动程序

->__u16 match_flags(确定设备和结构体中下列字段中哪一个相匹配)
->__u16 idVendor(设备的usb制造商id)
->__u16 idProduct(设备的usb产品id) 


4、USB骨架程序的关键几点如下:

a -- USB驱动的注册和注销 

   Usb驱动程序在注册时会发送一个命令给usb_register,通常在驱动程序的初始化函数里。

   当要从系统卸载驱动程序时,需要注销usb子系统。即需要usb_unregister 函数处理。

b -- 当usb设备插入时,为了使linux-hotplug(Linux中PCI、USB等设备热插拔支持)系统自动装载驱动程序,你需要创建一个MODULE_DEVICE_TABLE

代码如下(这个模块仅支持某一特定设备):

static struct usb_device_id skel_table [] = { 
    { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
    { } /* Terminating entry */};
 MODULE_DEVICE_TABLE (usb, skel_table);

 USB_DEVICE宏利用厂商ID和产品ID为我们提供了一个设备的唯一标识。当系统插入一个ID匹配的USB设备到USB总线时,驱动会在USB core中注册。驱动程序中probe 函数也就会被调用。usb_device 结构指针、接口号和接口ID都会被传递到函数中。

c -- static void * skel_probe(struct usb_device *dev,unsigned int ifnum, const struct usb_device_id *id)

       驱动程序需要确认插入的设备是否可以被接受,如果不接受,或者在初始化的过程中发生任何错误,probe函数返回一个NULL值。否则返回一个含有设备驱动程序状态的指针。通过这个指针,就可以访问所有结构中的回调函数。

d -- 在骨架驱动程序里,最后一点是我们要注册devfs

      我们创建一个缓冲用来保存那些被发送给usb设备的数据和那些从设备上接受的数据,同时USB urb 被初始化,并且我们在devfs子系统中注册设备,允许devfs用户访问我们的设备。注册过程如下:

/* initialize the devfs node for this device and register it */
	sprintf(name, "skel%d", skel->;minor);
	skel->devfs = devfs_register (usb_devfs_handle, name,DEVFS_FL_DEFAULT, 		USB_MAJOR,USB_SKEL_MINOR_BASE + skel->minor,
			S_IFCHR | S_IRUSR | S_IWUSR |S_IRGRP | S_IWGRP | S_IROTH, &skel_fops, NULL);
如果devfs_register函数失败,不用担心,devfs子系统会将此情况报告给用户。

当然最后,如果设备从usb总线拔掉,设备指针会调用disconnect 函数。驱动程序就需要清除那些被分配了的所有私有数据、关闭urbs,并且从devfs上注销调自己。
  /* remove our devfs node */devfs_unregister(skel->;devfs);


5、其他

a -- struct usb_host_endpoint(描述usb端点)

→(包含)struct usb_endpoint_descriptor(含真正端点信息,数据格式,是真正驱动关心的字段)

 端点描述符:

bEndpointAddress = 81(in)(第8位为1是输入设备)(usb的端点地址,包含端点方向)
bmAttibutes = 03(interrupt)(端点类型,为中断传输)
wMaxPacketSize = 0008(每次传8个字节)(端点每次可处理最大字节长度)
bInterval = 08(8ms)(如端点为中断,该值为轮询间隔)

b -- usb端点捆绑为接口,usb接口只处理一种usb逻辑连接,如鼠标键盘等

   一个usb设备可有多接口,usb扬声器:一个usb键盘用于按键,一个usb音频流,则需两个不同的驱动程序。

   usb驱动 通常将struct usb_interface 转成 struct usb_device 用函数 interface_to_usbdev转 

c -- struct usb_interface 描述usb接口

   →struct usb_host_interface * altsetting(接口结构体数组,包含所有可能用于该接口的可选设置)
    →struct usb_host_endpoint
   →unsigned num_altsetting(可选设置的数量)
   →struct usb_host_interface * cur_altsetting(接口当前活动设置)
   →int minor(usb核心分配给接口的次设备号,成功调用usb_register_dev有效) 

d -- usb设备非常复杂,由许多不同逻辑单元组成,简单关系如下:

   设备通常有一个以上的配置
   配置经常有一个以上接口
   接口通常有一个以上设置
   接口通常有一个以上端点
   设备描述-》配置描述-》接口描述-》端点描述 

e -- usb sysfs设备命名方案

   根集线器-集线器端口号:配置。接口
   对于usb hub树中层次更高的字树命名方案
   根集线器-集线器端口号-集线器端口号:配置。接口 

f --  linux内核的代码通过一个成为urb(usb请求块)和所有usb设备通信.  

 用struct urb描述(include/linux/usb.h中定义) 

   ->urb用异步同usb设备特定usb端点发送/接收数据,使用类似网络代码中的struct skbuff
   -> urb 被动态创建,随时可被驱动程序或usb核心取消,内部有引用计数,可被多次调用,使他们可在最后一个使用者释放他们时自动地销毁
   -> urb使得流处理或其他复杂的重叠的通信成为可能,获得高数据传输速度。 
   ->usb_alloc_urb() 创建urb包 usb_free_urb() 释放urb包 
   ->usb_fill_int_urb()正确初始化将发送到usb设备的中断端点urb
     usb_fill_bulk_urb() .. .. .. ... 批量传输端点urb
     usb_fill_control_urb() .. .. .. ... 控制端点urb
     等时urb在提交给核心时必须手动初始化(很不幸,没函数)
   ->usb_submit_urb()urb被usb驱动正确创建和初始化后,就可提交到usb核心,发送到usb设备上了,如果调用成功,函数返0,urb控制权转给usb核心
   ->usb_kill_urb() or usb_unlink_urb()取消已经被提交给核心的urb 


五、USB驱动开发简单示例

1、嵌入式Linux系统中USB摄像头驱动程序实现

     通常USB设备类驱动程序需要提供两个数据结构接口,一个针对USBD层,一个针对文件系统。USB摄像头驱动程序需要做的第一件事情就是在USB子系统里注册,并提供一些相关信息,包括该驱动程序支持哪些设备,当被支持的设备从总线插入或拔出时,会有哪些动作等,所有这些信息通过usb_driver的形式传送到USBD中,具体实现如下:

static struct usb_driver cam_driver = {
	.name: "cam_video",
	.probe: cam_probe,
	.disconnect: cam_disconnect,
	.id_table: cam_ids,
};	

其中

cam_video是客户端驱动程序的字符串名称,用于避免驱动程序的重复安装和卸载;

cam_probe则指向USB驱动程序的探测函数指针,提供给USB内核的函数用于判断驱动程序是否能对设备的某个接口进行驱动

cam_disconnect指向USB驱动程序中的断开函数的指针,当从系统中被移除或者驱动程序正在从USB核心中卸载时,USB核心将调用该函数;

cam_ids列表包含了一系列该驱动程序可以支持的所有不同类型的USB设备,如没有设置该列表,则该驱动程序中的探测回调函数不会被调用。

       当一个摄像头连接到USB总线上时,USB内核通过调用camDrive.c中的cam_probe函数判断是否支持该设备,如果支持,为该设备创建设备文件节点,以后应用程序就可以通过标准POSIX函数,把该设备当成普通文件来访问。摄像头驱动程序定义的文件系统接口如下:

struct file_operations cam_fops = {
	.owner     = THIS_MODULE,
	.open      = cam_v 4l2_open,
	.release   = cam_v4l2_release,
	.ioctl     = cam_v4l2_ioctl,
	.llseek    = no_llseek,
	.read      = cam_v4l2_read,
	.mmap      = cam_v4l2_mmap,
	.poll      = cam_v4l2_poll,
};
     在USB摄像头驱动程序的初始化函数中,通过usb_register进行设备注册;当从系统卸载驱动程序时,需要通过usb_deregister进行卸载。当驱动程序向USB子系统注册后,插入一个新的USB设备后总是要调用cam_probe函数进行设备驱动程序的查找,以确定新的USB设备硬件中的生产厂商ID和产品自定义ID是否与驱动程序相符,从而确定是否使用该驱动程序。


 2、USB摄像头驱动程序测试

       在嵌入式Linux系统中,USB摄像头被注册为一个标准的视频设备/dev/video,通过影像设备API接口Video4Linux来获取视频和音频数据。

现有的Video4Linux有两个版本:v4l和v4l2。通过v4l2 API接口获取视频图像的主要操作步骤如下:

a -- 打开视频设备

在Linux系统中,摄像头的设备文件为/dev/video0,调用系统函数open打开该设备。

fd = open (dev_name, O_RDWR);

b -- 获取视频设备所支持的V4L2特性

      所有的V4L2设备驱动都需要支持VIDIOC_QUERYCAP_ioctl的系统调用。通过该调用,确定该驱动程序是否与V4L2规范相兼容,同时获取该设备所支持的V4L2特性。在摄像头应用程序的开发过程中,需要判定该设备是否支持视频捕获。

ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);

c -- 获取视频设备支持的各种特性

      接着,利用ioctl(fd,VIDIOC_QUERYCAP,&cap)函数读取struct v4l2_capability中有关摄像头的信息。该函数成功返回后,这些信息从内核空间拷贝到用户程序空间capability各成员分量中。

ioctl(device_fd, VIDIOCGCAP, &vidcap);

d -- 设置视频捕获的图像格式

memset(&fmt, 0, sizeof(struct v4l2_format));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = vd->width;
fmt.fmt.pix.height = vd->height;
fmt.fmt.pix.pixelformat = vd->formatIn;
ret = ioctl(fd, VIDIOC_S_FMT, &fmt);

e -- 视频数据帧捕获

ioctl (fd, VIDIOC_DQBUF, &buf);

获取到视频数据之后,放到buf缓冲区中,通过QT桌面应用开发系统,显示到LCD显示屏上,通过触摸屏进行交互控制。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux USB 驱动开发(五)—— USB驱动程序开发过程简单总结 的相关文章

  • tcpdump 是否受 iptables 过滤影响?

    如果我的开发机器有iptables规则到FORWARD一些数据包 这些数据包是否被 tcpdump 捕获 我有这个问题 因为我知道存在其他链称为INPUT如果数据包路由到 它会过滤发往应用程序的数据包FORWARD链 它会到达吗tcpdum
  • Linux 上的静态 Qt5 构建:部署时如何处理字体?

    我使用这些配置选项创建了 Qt 5 2 0 库的静态版本 Ubuntu 12 04 开源 确认许可 force pkg config 发布 静止的 前缀 home juzzlin qt5 无icu opengl桌面 无油嘴滑舌 辅助功能 n
  • linux-x64 二进制文件无法在 linuxmusl-x64 平台上使用错误

    我正在安装Sharp用于使用 package json 的 Nodejs 项目的 docker 映像上的映像压缩包 当我创建容器时 我收到有关 Sharp 包的以下错误 app node modules sharp lib libvips
  • 执行“minikube start”命令时出现问题

    malik malik minikube start minikube v1 12 0 on Ubuntu 18 04 Using the docker driver based on existing profile Starting c
  • 如何阻止ubuntu在使用apt安装或更新软件包时弹出“Daemons using outdatedlibraries”? [关闭]

    Closed 这个问题是与编程或软件开发无关 help closed questions 目前不接受答案 我最近新安装了 Ubuntu 22 04 LTS 我发现每次使用 apt 安装或更新软件包时 它都会询问我有关Which servic
  • 尽管 if 语句,Visual Studio 仍尝试包含 Linux 标头

    我正在尝试创建一个强大的头文件 无需更改即可在 Windows 和 Linux 上进行编译 为此 我的包含内容中有一个 if 语句 如下所示 if defined WINDOWS include
  • CMake 链接 glfw3 lib 错误

    我正在使用 CLion 并且正在使用 glfw3 库编写一个程序 http www glfw org docs latest http www glfw org docs latest 我安装并正确执行了库中的所有操作 我有 a 和 h 文
  • 使用包管理器时如何管理 Perl 模块?

    A 最近的问题 https stackoverflow com questions 397817 unable to find perl modules in intrepid ibex ubuntu这让我开始思考 在我尝试过的大多数 Li
  • .net-core:ILDASM / ILASM 的等效项

    net core 是否有相当于 ILDASM ILASM 的功能 具体来说 我正在寻找在 Linux 上运行的东西 因此为什么是 net core ildasm 和 ilasm 工具都是使用此存储库中的 CoreCLR 构建的 https
  • Intel 上的 gcc 中的 _mm_pause 用法

    我参考过这个网页 https software intel com en us articles benefitting power and performance sleep loops https software intel com
  • 为什么opencv videowriter这么慢?

    你好 stackoverflow 社区 我有一个棘手的问题 我需要你的帮助来了解这里发生了什么 我的程序从视频采集卡 Blackmagic 捕获帧 到目前为止 它工作得很好 同时我用 opencv cv imshow 显示捕获的图像 它也工
  • 使用 python 脚本更改 shell 中的工作目录

    我想实现一个用户态命令 它将采用其参数之一 路径 并将目录更改为该目录 程序完成后 我希望 shell 位于该目录中 所以我想实施cd命令 但需要外部程序 可以在 python 脚本中完成还是我必须编写 bash 包装器 Example t
  • 使用 gdb 调试 Linux 内核模块

    我想知道 API 在内核模块 中返回什么 从几种形式可以知道 这并不是那么简单 我们需要加载符号表来调试内核模块 所以我所做的就是 1 尝试找到内核模块的 text bss和 data段地址 2 在 gdb 中使用 add symbol f
  • cdc_acm:无法设置 dtr/rts - 无法与 USB cdc 设备通信

    我试图使用 pic24fj128gb206 枚举 usb cdc 设备 设备似乎已正确枚举 但是当我将设备连接到 Linux PC 时 我从内核收到以下警告消息 cdc acm 1 8 1 6 7 1 0 failed to set dtr
  • 检查已安装的软件包,如果没有找到则安装

    我需要检查已安装的软件包 如果未安装则安装它们 RHEL CentOS Fedora 示例 rpm qa grep glibc static glibc static 2 12 1 80 el6 3 5 i686 如何在 BASH 中进行检
  • 为什么同一个curl命令在windows和linux下输出不同的东西?

    为什么同样的curl o file https www link com 命令输出不同的东西 例如 如果我运行命令curl o source txt https www youtube com playlist list PLIx6Fwnp
  • 从 Linux 内核模块中调用用户空间函数

    我正在编写一个简单的 Linux 字符设备驱动程序 以通过 I O 端口将数据输出到硬件 我有一个执行浮点运算的函数来计算硬件的正确输出 不幸的是 这意味着我需要将此函数保留在用户空间中 因为 Linux 内核不能很好地处理浮点运算 这是设
  • 如何在shell脚本中给出密码?

    在 shell 脚本文件中 我使用一些命令 例如scp and make install要求我输入密码 我运行一个 shell 脚本来编译一个大项目 一段时间后它会要求我输入密码才能使用scp 我需要等待该过程并在此之后提供密码 我只想通过
  • 从核心转储中获取堆栈跟踪

    如何从核心转储文件中获取堆栈跟踪 该文件大约 14 mb 是在我的应用程序退出并显示 分段错误 后生成的 我使用的是红帽 5 5 gdb usr bin myapp binary corefile 然后 使用以下之一 gdb bt gdb
  • 如何找到进程启动时使用的原始用户名?

    有一个 perl 脚本需要以 root 身份运行 但我们必须确保运行该脚本的用户最初没有以用户 foo 身份登录 因为它将在脚本运行期间被删除 那么 我如何查明自登录以来可能已多次起诉的用户是否在该链中的任何时间都没有模拟过 foo 我发现

随机推荐

  • Redis源码-事件库

    网上看了很多Redis事件库的解读 xff0c 自己也研究了好几遍 xff0c 还是记录下来 xff0c 虽然水平有限 xff0c 但是进步总会是有的 网络事件库封装了Epoll的操作 xff08 当然是指Linux下的多路复用了 xff0
  • Redis源码分析-内存数据结构intset

    这次研究了一下intset xff0c 研究的过程中 xff0c 一度看不下过去 xff0c 但是还是咬牙挺过来了 xff0c 看懂了也就是那么回事 xff0c 静下心来 xff0c 切莫浮躁 Redis为了追求高效 xff0c 在存储下做
  • 基于STM32实验:uC/OS-III操作系统移植并创建多任务系统实验

    本次实验内容 xff1a 将uC OS III操作系统移植到stm32F103C8T6上 xff0c 构建至少3个任务 xff08 task xff09 xff1a 分别以1s和3s周期对LED灯进行点亮 熄灭的控制 xff1b 另外一个t
  • 初级软件测试面试题汇总

    一 请描述如何划分缺陷与错误严重性和优先级别 xff1f 给软件缺陷与错误划分严重性和优先级的通用原则 xff1a xff08 1 xff09 表示软件缺陷所造成的危害和恶劣程度 xff08 2 xff09 优先级表示修复缺陷的重要程度和次
  • selenium 安装

    火狐浏览器版本 xff1a 35 0 1 1 官网安装 Version 2 9 1 1 通过官网安装插件 xff1a https addons mozilla org en GB firefox addon selenium ide ver
  • 构造函数与析构函数执行顺序

    构造函数与析构函数执行顺序 代码 xff1a include lt iostream gt using namespace std class ABCD public ABCD int a int b int c this gt a 61
  • java.lang.NoSuchMethodException异常

    在Struts2中 xff0c 有时候会出现java lang NoSuchMethodException异常 xff0c 有可能是三种情况导致的运行异常 xff1a 第一种 xff1a Action 类的方法被定义成 private 类型
  • java.lang.IllegalArgumentException异常解决

    在maven项目中测试代码的时候 xff0c 碰到java lang IllegalArgumentException 异常 xff1a 严重 Servlet service for servlet e3 manager in contex
  • 在idea中创建一个普通工程

    第一步 xff1a File gt new gt Project 第二步 xff1a 点击next 点击 finish 即可 xff01 xff01 xff01 运行结果
  • java:获取当月最后一天

    设置时间格式 SimpleDateFormat format 61 new SimpleDateFormat 34 yyyy MM dd 34 获得实体类 Calendar ca 61 Calendar getInstance 设置最后一天
  • idea自动生成UUID和解决办法

    正常情况下 xff0c 鼠标点击类名 xff0c Alt 43 Insert键就会出现生成UUID选项 xff0c 即 xff1a 有时候Alt 43 Insert没有UUID选项 xff0c 解决办法 第一种情况 xff1a Settin
  • 页面<div>位置调整

    调整页面 lt div gt 样式 给 lt div gt lt select gt 分别起名字 xff1a div2 xff0c s1 lt div gt 代码 xff1a lt div class 61 34 div2 34 style
  • 马士兵_JAVA自学之路(为那些目标模糊的码农们)

    转载自 xff1a https blog csdn net anlidengshiwei article details 42264301 JAVA自学之路 一 学会选择 为了就业 xff0c 不少同学参加各种各样的培训 决心做软件的 xf
  • 在深度学习中Softmax交叉熵损失函数的公式求导

    以下部分基本介绍转载于点击打开链接 在深度学习NN中的output层通常是一个分类输出 xff0c 对于多分类问题我们可以采用k 二元分类器来实现 xff0c 这里我们介绍softmax softmax回归中 xff0c 我们解决的是多分类
  • 1-基于ArUco码的标记与检测

    1 简介 姿态估计 xff08 Pose estimation xff09 在 计算机视觉领域扮演着十分重要的角色 xff1a 机器人导航 增强现实以及其它 这一过程的基础是找到现实世界和图像投影之间的对应点 这通常是很困难的一步 xff0
  • 4-基于ArUco相机姿态评估

    1 简介 基于ArUco评估相机姿态 xff0c 可以使用OPENCV的外部库 xff08 opencv contrib xff09 中的aruco模块 xff0c 可以参考安装目录 xff08 库目录 xff09 xff1a opencv
  • MySQL--40道基础概念选择题及答案

    一 单选题 xff08 题数 xff1a 40 xff0c 共 40 0 分 xff09 1 在计算机系统中能够实现对数据库资源进行统一管理和控制的是 xff08 A xff09 A DBMS B DBA C DBS D DBAS 2 数据
  • 抽象类方法——子类定义getDescription方法返回对一个人的简单描述

    Person与子类的关系图 每一个 人都有一些诸如名字这样的属性 xff0c 学生与雇员都有名字属性 xff0c 因此可以将getName方法放在位于继承关系较高层的通用超类 xff08 父类 xff09 中 xff0c 现在增加一个get
  • Exynos4412 Uboot 移植(一)—— Uboot 编译流程分析

    Uboot 所用版本 u boot 2013 01 u boot 2013 01 中有上千文件 xff0c 要想了解对于某款开发板 xff0c 使用哪些文件 哪些文件首先执行 可执行文件占用内存的情况 xff0c 最好的方法就是阅读它的Ma
  • Linux USB 驱动开发(五)—— USB驱动程序开发过程简单总结

    设备驱动程序是操作系统内核和机器硬件之间的接口 xff0c 由一组函数和一些私有数据组成 xff0c 是应用程序和硬件设备之间的桥梁 在应用程序看来 xff0c 硬件设备只是一个设备文件 xff0c 应用程序可以像操作普通文件一样对硬件设备