目录

一、V4L2简介

二、V4L2操作流程

1.打开摄像头

2.查询设备的属性/能力/功能

3.获取摄像头支持的格式

4.设置摄像头的采集通道

5.设置/获取摄像头的采集格式和参数

6.申请帧缓冲、内存映射、入队

(1)申请帧缓冲

(2)内存映射

(3)入队

7.开启视频采集

8.读取数据、对数据进行处理

9.结束视频采集

三、应用编程


一、V4L2简介

V4L2(Video for linux two)是 Linux 内核中视频类设备的一套驱动框架,为视频类设备驱动开发和应用层提供了一套统一的接口规范。使用 V4L2 设备驱动框架注册的设备会在 Linux 系统/dev/目录下生成对应的设备节点文件,设备节点的名称通常为 videoX(X 标准一个数字编号:/dev/videox),每一个 videoX 设备文件就代表一个视频类设备。应用程序通过对 videoX 设备文件进行 I/O 操作来配置、使用设备类设备。

二、V4L2操作流程

V4L2摄像头驱动框架的访问是通过系统IO的接口 ------ ioctl函数,ioctl专用于硬件控制的系统IO的接口。

#include <sys/ioctl.h>     //包含头文件
int ioctl(int fd, unsigned long request, ...);
fd: 文件描述符
request: 此参数与具体要操作的对象有关, 表示向文件描述符请求相应的操作
...: 可变参函数, 第三个参数需要根据 request 参数来决定,配合 request 来使用
返回值: 成功返回 0,失败返回-1

ioctl()是一个文件 IO 操作的杂物箱,可以处理的事情非常杂、不统一,一般用于操作特殊文件或硬件外设,可以通过 ioctl 获取外设相关信息。通过 ioctl()来完成,搭配不同的 V4L2 指令(request
参数)请求不同的操作,这些指令定义在头文件 linux/videodev2.h 中,常用的如下图。

1.打开摄像头

视频类设备对应的设备节点为/dev/videoX, X 为数字编号,通常从 0 开始,调用 open 打开,得到文件描述符 fd。

int fd = -1;
fd = open("/dev/video0", O_RDWR);
if (0 > fd)
{perror( "open error");exit(-1);
}

2.查询设备的属性/能力/功能

打开设备后,需要查询设备的属性,确定该设备是否是一个视频采集类设备。通过 ioctl()将获取到一个 struct v4l2_capability 类型数据, struct v4l2_capability 数据结构描述了设备的一些属性,结构体定义如下:

struct v4l2_capability {__u8 driver[16]; /* 驱动的名字 */__u8 card[32]; /* 设备的名字 */__u8 bus_info[32]; /* 总线的名字 */__u32 version; /* 版本信息 */__u32 capabilities; /* 设备拥有的能力 */__u32 device_caps;__u32 reserved[3]; /* 保留字段 */
};

这里关注capabilities 字段,该字段描述了设备拥有的能力,该字段的值如下

所以可以通过判断 capabilities字段是否包含 V4L2_CAP_VIDEO_CAPTURE、 来确定它是否是一个摄像头设备。

struct v4l2_capability cap = {};
ioctl(fd,VIDIOC_QUERYCAP,&cap);
if(cap.capabilities&V4L2_CAP_VIDEO_CAPTURE)
{//是一个摄像头
}

3.获取摄像头支持的格式

获取支持的像素格式使用 VIDIOC_ENUM_FMT 指令

struct v4l2_fmtdesc {__u32           index;             /*格式编号*/__u32            type;              /*摄像头的格式  V4L2_BUF_TYPE_VIDEO_CAPTURE*/__u32               flags;__u8            description[32];   /*描述信息:描述 pixelformat 像素格式。*/__u32            pixelformat;       /*类型格式 --- 4字节:像素格式编号*/__u32          reserved[4];
};

pixelfoemat像素格式:

//定义格式的宏
#define v4l2_fourcc(a, b, c, d)\((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24))#define V4L2_PIX_FMT_YUYV    v4l2_fourcc('Y', 'U', 'Y', 'V')  /* 16 YUV 4:2:2 */
#define V4L2_PIX_FMT_YVYU    v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16 YVU 4:2:2 */
#define V4L2_PIX_FMT_MJPEG   v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG */
#define V4L2_PIX_FMT_JPEG    v4l2_fourcc('J', 'P', 'E', 'G') /* JFIF JPEG   */ 

v4l2_fourcc是 宏定义,通过这个宏以及对应的参数合成的一个u32 类型数据。

type类型:

获取设备的哪种功能对应的像素格式, 因为有些设备它可能即支持视频采集功能、又支持视频输出等其它的功能;

enum v4l2_buf_type {V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, //视频采集V4L2_BUF_TYPE_VIDEO_OUTPUT = 2, //视频输出V4L2_BUF_TYPE_VIDEO_OVERLAY = 3,V4L2_BUF_TYPE_VBI_CAPTURE = 4,V4L2_BUF_TYPE_VBI_OUTPUT = 5,V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6,V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7,V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10,V4L2_BUF_TYPE_SDR_CAPTURE = 11,V4L2_BUF_TYPE_SDR_OUTPUT = 12,V4L2_BUF_TYPE_META_CAPTURE = 13,/* Deprecated, do not use */V4L2_BUF_TYPE_PRIVATE = 0x80,
};

type 字 段 需 要 在 调 用 ioctl() 之 前 设 置 它 的 值 , 对 于 摄 像 头 , 需 要 将 type 字 段 设 置 为V4L2_BUF_TYPE_VIDEO_CAPTURE,指定将要获取的是视频采集的像素格式。

struct v4l2_fmtdesc fmt = {};
fmt.index = 0;//第一种格式
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//获取摄像头的格式
ioctl(fd,VIDIOC_ENUM_FMT,&fmt);

4.设置摄像头的采集通道

int index = 0;//使用通道0
ioctl(fd,VIDIOC_S_INPUT,&index);

5.设置/获取摄像头的采集格式和参数

使用 VIDIOC_G_FMT 指令查看设备当期的格式和使用 VIDIOC_S_FMT 指令设置设备的格式;

int ioctl(int fd, VIDIOC_G_FMT, struct v4l2_format *fmt);
int ioctl(int fd, VIDIOC_S_FMT, struct v4l2_format *fmt);

ioctl()会将获取到的数据写入到 fmt 指针所指向的对象中或者会使用 fmt 所指对象的数据去设置设备的格式, struct v4l2_format 结构体描述了格式相关的信息。

struct v4l2_format {__u32     type;//V4L2_BUF_TYPE_VIDEO_CAPTUREunion {struct v4l2_pix_format        pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */struct v4l2_pix_format_mplane pix_mp;  /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */struct v4l2_window     win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */struct v4l2_vbi_format        vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */struct v4l2_sliced_vbi_format   sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */struct v4l2_sdr_format       sdr;     /* V4L2_BUF_TYPE_SDR_CAPTURE */__u8    raw_data[200];                   /* user-defined */} fmt;
};struct v4l2_pix_format {__u32                 width;//像素宽度__u32           height;//像素高度__u32          pixelformat;//采集格式 V4L2_PIX_FMT_YVYU__u32           field;      /* V4L2_FIELD_NONE */__u32              bytesperline;   /* for padding, zero if unused */__u32                  sizeimage;__u32         colorspace; /* enum v4l2_colorspace */__u32         priv;       /* private data, depends on pixelformat */__u32         flags;      /* format flags (V4L2_PIX_FMT_FLAG_*) */__u32           ycbcr_enc;  /* enum v4l2_ycbcr_encoding */__u32         quantization;   /* enum v4l2_quantization */__u32           xfer_func;  /* enum v4l2_xfer_func */
};

type 字段依然与前面介绍的结构体中的 type 字段意义相同,不管是获取格式、还是设置格式都需要在调用 ioctl()函数之前设置它的值。接下来是一个 union 共用体,当 type 被设置为V4L2_BUF_TYPE_VIDEO_CAPTURE 时, pix 变量生效,它是一个 struct v4l2_pix_format 类型变量,记录了视频帧格式相关的信息。

获取当前的格式、并设置格式:

struct v4l2_format format = {};
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width = 640;
format.fmt.pix.height = 480;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
format.fmt.pix.field= V4L2_FIELD_NONE ;ioctl(fd,VIDIOC_S_FMT,&format); //S:set

6.申请帧缓冲、内存映射、入队

(1)申请帧缓冲

读取摄像头数据的方式有两种:一种是 read 方式,直接通过 read()系统调用读取摄像头采集到的数据;另一种是 streaming 方式。使用 VIDIOC_QUERYCAP 指令查询设备的属性、得到一个 struct v4l2_capability 类型数据, 其中 capabilities 字段记录了设备拥有的能力,当该字段包含
V4L2_CAP_READWRITE 时,表示设备支持 read I/O 方式读取数据;当该字段包含V4L2_CAP_STREAMING时,表示设备支持 streaming I/O 方式;使用streaming I/O 方式,需要向设备申请帧缓冲,并将帧缓冲映射到应用程序进程地址空间中。

使用 VIDIOC_REQBUFS 指令可申请帧缓冲:

ioctl(int fd, VIDIOC_REQBUFS, struct v4l2_requestbuffers *reqbuf);

调用 ioctl()需要传入一个 struct v4l2_requestbuffers *指针, struct v4l2_requestbuffers 结构体描述了申请帧缓冲的信息, ioctl()会根据 reqbuf 所指对象填充的信息进行申请。

struct v4l2_requestbuffers {__u32            count;//缓冲区块数 ----- 4__u32          type;       /* enum v4l2_buf_type */__u32           memory;     /* enum v4l2_memory */__u32         reserved[2];
};

type 字段与前面所提及到的 type 字段意义相同,count 字段用于指定申请帧缓冲的数量。
memory 字段可取值如下:

enum v4l2_memory {V4L2_MEMORY_MMAP = 1,V4L2_MEMORY_USERPTR = 2,V4L2_MEMORY_OVERLAY = 3,V4L2_MEMORY_DMABUF = 4,
};

通常将 memory 设置为 V4L2_MEMORY_MMAP 。
申请缓存:

struct v4l2_requestbuffers req = {};
req.count = 4;     //申请4个帧缓存
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;ioctl(fd,VIDIOC_REQBUFS,&req);

streaming I/O 方式会在内核空间中维护一个帧缓冲队列, 驱动程序会将从摄像头读取的一帧数据写入到队列中的一个帧缓冲,接着将下一帧数据写入到队列中的下一个帧缓冲;当应用程序需要读取一帧数据时,需要从队列中取出一个装满一帧数据的帧缓冲,这个取出过程就叫做出队;当应用程序处理完这一帧数据后,需要再把这个帧缓冲加入到内核的帧缓冲队列中,这个过程叫做入队。

使用 VIDIOC_REQBUFS 指令申请帧缓冲, 该缓冲区是由内核所维护的,应用程序不能直接读取该缓冲区的数据,需要将其映射到用户空间中,应用程序读取映射区的数据实际上是读取内核维护的帧缓冲中的数据。

(2)内存映射

在映射之前,需要查询帧缓冲的信息:帧缓冲的长度、偏移量。使用VIDIOC_QUERYBUF指令查询:

ioctl(int fd, VIDIOC_QUERYBUF, struct v4l2_buffer *buf);

调用 ioctl()需要传入一个 struct v4l2_buffer *指针, struct v4l2_buffer 结构体描述了帧缓冲的信息, ioctl()会将获取到的数据写入到 buf 指针所指的对象中。

struct v4l2_buffer {__u32            index;//编号__u32         type;//V4L2_BUF_TYPE_VIDEO_CAPTURE__u32         bytesused;//使用的字节数__u32         flags;__u32         field;struct timeval        timestamp;struct v4l2_timecode  timecode;__u32          sequence;/* memory location */__u32         memory;//V4L2_MEMORY_MMAPunion {__u32           offset;//偏移unsigned long   userptr;struct v4l2_plane *planes;__s32      fd;} m;__u32            length;//长度__u32            reserved2;__u32         reserved;
};

index 字段表示一个编号, 申请的多个帧缓冲、 每一个帧缓冲都有一个编号,从 0 开始。一次 ioctl()调用只能获取指定编号对应的帧缓冲的信息,所以要获取多个帧缓冲的信息,需要重复调用多次,每调用一次ioctl()、 index 加 1,指向下一个帧缓冲。

type 字段和memory 字段与前面介绍的一样。length 字段表示帧缓冲的长度,共同体中的 offset 表示帧缓冲的偏移量。因为应用程序通过 VIDIOC_REQBUFS 指令申请帧缓冲时,内核会向操作系统申请一块内存空间作为帧缓冲区,内存空间的大小就等于申请的帧缓冲数量 * 每一个帧缓冲的大小,每一个帧缓冲对应到这一块内存空间的某一段,所以它们都有一个地址偏移量。

申请帧缓冲后、调用 mmap()将帧缓冲映射到用户地址空间

struct v4l2_buffer buf = {};
buf.index = xxx;//0~3
but.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;ioctl(fd,VIDIOC_QUERYBUF,&buf);
//映射到用户空间
mmap(NULL,buf.length,.......,fd,buf.m.offset);

(3)入队

使用 VIDIOC_QBUF 指令将帧缓冲放入到内核的帧缓冲队列中,调用 ioctl()之前,需要设置 struct v4l2_buffer 类型对象的 memory、 type 字段

ioctl(fd,VIDIOC_QBUF,&buf);

7.开启视频采集

使用 VIDIOC_DQBUF 指令开启视频采集,

ioctl(int fd, VIDIOC_STREAMON, int *type); //开启视频采集
ioctl(int fd, VIDIOC_STREAMOFF, int *type); //停止视频采集
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd,VIDIOC_STREAMON,&buf_type);

8.读取数据、对数据进行处理

开启视频采集之后,便可以去读取数据 ,直接读取每一个帧缓冲的在用户空间的映射区即可读取到摄像头采集的每一帧图像数据。在读取数据之前,需要将帧缓冲从内核的帧缓冲队列中取出,这个操作叫做帧缓冲出队。帧缓冲出队,便可读取数据,对数据进行处理:将摄像头采集的图像显示到 LCD屏上;数据处理完成之后,再将帧缓冲入队,往复操作。

使用 VIDIOC_DQBUF 指令执行出队操作

ioctl(int fd, VIDIOC_DQBUF, struct v4l2_buffer *buf);
while(1){//从采集队列中取出一帧ioctl(fd,VIDIOC_DQBUF,&buf);//将该帧的数据拷贝走memcpy(....);//将取出的一帧放回队列ioctl(fd,VIDIOC_QBUF,&buf);//显示,存储......
}

9.结束视频采集

结束视频采集,使用 VIDIOC_STREAMOFF 指令

enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd,VIDIOC_STREAMOFF,&buf_type);//解除映射
//关闭设备文件

三、应用编程

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <sys/mman.h>
#include <string.h>typedef struct{char *start;size_t length;
}buffer_t;buffer_t buffer[4];
buffer_t current;//保存当前取出的一帧int lcd_fd;
int *memp;unsigned int sign3 = 0;int yuyv2rgb(int y, int u, int v)
{unsigned int pixel24 = 0;unsigned char *pixel = (unsigned char *)&pixel24;int r, g, b;static int  ruv, guv, buv;if(sign3){sign3 = 0;ruv = 1159*(v-128);guv = 380*(u-128) + 813*(v-128);buv = 2018*(u-128);}r = (1164*(y-16) + ruv) / 1000;g = (1164*(y-16) - guv) / 1000;b = (1164*(y-16) + buv) / 1000;if(r > 255) r = 255;if(g > 255) g = 255;if(b > 255) b = 255;if(r < 0) r = 0;if(g < 0) g = 0;if(b < 0) b = 0;pixel[0] = r;pixel[1] = g;pixel[2] = b;return pixel24;
}int yuyv2rgb0(unsigned char *yuv, unsigned char *rgb, unsigned int width, unsigned int height)
{unsigned int in, out;int y0, u, y1, v;unsigned int pixel24;unsigned char *pixel = (unsigned char *)&pixel24;unsigned int size = width*height*2;for(in = 0, out = 0; in < size; in += 4, out += 6){y0 = yuv[in+0];u  = yuv[in+1];y1 = yuv[in+2];v  = yuv[in+3];sign3 = 1;pixel24 = yuyv2rgb(y0, u, v);rgb[out+0] = pixel[0];    rgb[out+1] = pixel[1];rgb[out+2] = pixel[2];pixel24 = yuyv2rgb(y1, u, v);rgb[out+3] = pixel[0];rgb[out+4] = pixel[1];rgb[out+5] = pixel[2];}return 0;
}//LCD初始化
void lcd_init()
{lcd_fd = open("/dev/fb0",O_RDWR);if(lcd_fd==-1){perror("open");;exit(-1);}//映射memp = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd_fd,0);if(memp==MAP_FAILED){perror("mmap");;exit(-1);}
}void lcd_uninit()
{munmap(memp,800*480*4);close(lcd_fd);
}int main()
{lcd_init();//1.打开摄像头int fd = open("/dev/video7",O_RDWR);if(fd==-1){perror("open");exit(-1);}//2.获取功能参数struct v4l2_capability cap = {};int res = ioctl(fd,VIDIOC_QUERYCAP,&cap);if(res==-1){perror("ioctl cap");exit(-1);}if(cap.capabilities&V4L2_CAP_VIDEO_CAPTURE){//设备是一个摄像头printf("capture device!\n");}else{printf("not a capture device!\n");exit(-1);}//3.获取摄像头支持的格式struct v4l2_fmtdesc fmt = {};fmt.index = 0;//第一种格式fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//获取摄像头的格式while((res=ioctl(fd,VIDIOC_ENUM_FMT,&fmt))==0){printf("pixformat=%c%c%c%c,description=%s\n",fmt.pixelformat&0xff,(fmt.pixelformat>>8)&0xff,(fmt.pixelformat>>16)&0xff,(fmt.pixelformat>>24)&0xff,fmt.description);fmt.index++;}//4.设置采集通道int index = 0;//使用通道0res = ioctl(fd,VIDIOC_S_INPUT,&index);if(res==-1){perror("ioctl s_input");exit(-1);}//5.设置摄像头的采集格式struct v4l2_format format = {};format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;format.fmt.pix.width = 640;format.fmt.pix.height = 480;format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//YUYV格式format.fmt.pix.field= V4L2_FIELD_NONE;res = ioctl(fd,VIDIOC_S_FMT,&format);if(res==-1){perror("ioctl s_fmt");exit(-1);}//6.申请缓存空间struct v4l2_requestbuffers req = {};req.count = 4;req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;res = ioctl(fd,VIDIOC_REQBUFS,&req);if(res==-1){perror("ioctl reqbufs");exit(-1);}//7.分配,映射,入队size_t i,max_len = 0;for(i=0;i<4;i++){struct v4l2_buffer buf = {};buf.index = i;//0~3buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;res = ioctl(fd,VIDIOC_QUERYBUF,&buf);if(res==-1){perror("ioctl querybuf");exit(-1);}//记录最大长度if(buf.length>max_len)max_len = buf.length;//映射buffer[i].length = buf.length;buffer[i].start = mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,buf.m.offset);if(buffer[i].start==MAP_FAILED){perror("mmap");exit(-1);}//入队res = ioctl(fd,VIDIOC_QBUF,&buf);if(res==-1){perror("ioctl qbuf");exit(-1);}}//申请临时缓冲区current.start = malloc(max_len);if(current.start==NULL){perror("malloc");exit(-1);}//8.启动摄像头enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;res = ioctl(fd,VIDIOC_STREAMON,&buf_type);if(res==-1){perror("ioctl streamon");exit(-1);}//延时sleep(1);//RGB缓冲区char rgb[640*480*3];//9.采集数据while(1){struct v4l2_buffer buf = {};buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;//出队res = ioctl(fd,VIDIOC_DQBUF,&buf);if(res==-1){perror("ioctl dqbuf");}//拷贝数据memcpy(current.start,buffer[buf.index].start,buf.bytesused);current.length = buf.bytesused;//入队res = ioctl(fd,VIDIOC_QBUF,&buf);if(res==-1){perror("ioctl qbuf");}//显示 保存 传输.....//YUYV转RGByuyv2rgb0(current.start,rgb,640,480);//显示到LCDint x,y;for(y=0;y<480;y++){for(x=0;x<640;x++){*(memp+y*800+x) = rgb[3*(y*640+x)]<<16 | rgb[3*(y*640+x)+1]<<8 | rgb[3*(y*640+x)+2];}}}//10.关闭摄像头采集buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;res = ioctl(fd,VIDIOC_STREAMOFF,&buf_type);if(res==-1){perror("ioctl streamoff");exit(-1);}//解除映射for(i=0;i<4;i++){munmap(buffer[i].start,buffer[i].length);}free(current.start);close(fd);lcd_uninit();return 0;
}

Linux之V4L2驱动框架相关推荐

  1. 深入学习Linux摄像头(二)v4l2驱动框架

    深入学习Linux摄像头系列 深入学习Linux摄像头(一)v4l2应用编程 深入学习Linux摄像头(二)v4l2驱动框架 深入学习Linux摄像头(三)虚拟摄像头驱动分析 深入学习Linux摄像头 ...

  2. Linux V4l2驱动 -- 框架概述

    V4l2框架简述 1 硬件 常用的电脑摄像头是USB接口,主流的智能手机摄像头是MIPI接口,另外还有像树莓派等硬件使用的CSI接口的设备.常用的智能手机Camera采用的MIPI接口,电路框架以及电 ...

  3. V4L2系列 之 V4L2驱动框架

    目录 前言 一.V4L2驱动框架概览 1.应用层 ->中间层->驱动层 2.主要代码文件(Linux 4.19版本内核) 二.怎么写V4L2驱动 1.如何写一个设备的驱动? 2.Video ...

  4. Camera 从应用层看V4L2驱动框架

    1.V4L2驱动框架简介 V4L2可用于采集图片.视频和音频数据的通用 API 接口,配合适当的视频采集设备和相应的驱 动程序,可以实现图片.视频.音频等的采集. 2.V4L2视频采集原理 当启动视频 ...

  5. windows linux 融合,Windows和Linux的设备驱动框架的对比融合研究

    摘要:把驱动框架分为三层,针对各层在Windows和Linux中的实现方法的不同,对Windows和Linux的设备驱动框架进行对比研究.从接口函数,应用程序访问驱动程序的路径,驱动程序具体实现及安装 ...

  6. Linux总线设备驱动框架的理解(非常棒的文章!)

    以下内容源于微信公众号:嵌入式企鹅圈.有格式内容上的修改,如有侵权,请告知删除. Linux的设备驱动框架,即某类设备对应的驱动的框架. 这里是"Linux总线设备驱动框架",应该 ...

  7. linux中spi驱动框架

    原 linux中spi驱动框架 2016年09月14日 15:57:06 andylauren 阅读数:403 <span class="tags-box artic-tag-box& ...

  8. Linux的 i2c 驱动框架分析

    1.基本概念 总线设备驱动模型,是Linux 内核的一个基础,基本理论可以说按照大企业的分工原则,每个人只要负责自己的事情,向其他部门给出标准的接口调用,后勤部就负责后勤工作,厨房有可能跟后勤部产生工 ...

  9. Linux下USB驱动框架分析【转】

    转自:http://blog.csdn.net/brucexu1978/article/details/17583407 版权声明:本文为博主原创文章,未经博主允许不得转载. http://www.c ...

最新文章

  1. Containerd 的前世今生和保姆级入门教程
  2. Netty - I/O模型之BIO
  3. 动态链接库和静态库的区别
  4. vba九九乘法表代码_【VBA实例】在立即窗口获得九九乘法口诀
  5. 吴恩达机器学习笔记:(三)梯度下降法
  6. 【深度学习】动漫风格迁移AnimeGANv2,发布线上运行Demo
  7. 分库分表全面了解分析
  8. 【Spark】Spark 3.0 支持 event logs 滚动
  9. 中期报告c语言,中期考核表汇报范本.doc
  10. 【收藏】李纪为:初入NLP领域的一些小建议
  11. The NVIDIA driver on your system is too old (found version 9000).
  12. 智能化监狱室内人员定位管理系统,RFID室内定位方案更加智能-新导智能
  13. 打印机无法打印测试页
  14. 【红帽入门指南】第二期:Linux的基本使用
  15. 传智播客与英特尔结成合作伙伴,共同推动软件技术进步
  16. 激活各种win 10 系统的方式
  17. java 二维表格_实现二维表
  18. 采用html 的a标签,href连接为文件时无法下载解决方案
  19. HQ-610型超声波多普勒流量计
  20. Scrapy:[scrapy.core.engine] DEBUG: Crawled (200)解决尝试

热门文章

  1. 网站收录链接分析之网站排名查询
  2. 程序员提问的智慧(How-To-Ask-Questions-The-Smart-Way)
  3. linux 命令行别名,bash命令行实用的别名-alias命令
  4. 气动四自由度机械手结构设计(设计说明书+CAD图纸) 套类零件自动上下料机构
  5. [计算机毕业设计]大数据的B站数据分析与可视化
  6. 果园篱笆c语言算法,天然篱笆墙:果园栽种这几种树
  7. 零基础学CocosCreator·第六季-常用编程框架和算法
  8. 《秋波媚·七月十六日晚登高兴亭望长安南山》 陆游
  9. 创意卡通风格会员日海报
  10. PostWoman(网页调试工具)