DAY12-Windows摄像头截屏,Linux摄像头和串口

400 阅读2分钟

Linux摄像头

摄像头的视频格式

常见摄像头的视频格式有很多种,比如RGB,YUYV,MJPEG......,获取到摄像头的图片流后需要按照格式做不同的处理。

Linux中摄像头的层次框架

0

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

0

摄像头的设备文件

Linux中V4L2摄像头对应的设备文件是 /dev/video0...1...2...

0

插入USB摄像头后

0

摄像头的设备文件 ----------- /dev/video7

V4L2摄像头的访问

头文件 -------------- #include <linux/videodev2.h>

开摄像头

int fd = open("/dev/video7",O_RDWR);

获取摄像头的功能参数

struct v4l2_capability {
	__u8	driver[16];
	__u8	card[32];
	__u8	bus_info[32];
	__u32   version;//内核版本
	__u32	capabilities;//功能参数
	__u32	device_caps;
	__u32	reserved[3];
};

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

获取摄像头支持的格式

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

//定义格式的宏
#define v4l2_fourcc(a, b, c, d)\
	((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24))
 
#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   */ 

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

image-20220826212415080

练习:

获取摄像头采集格式,是不是支持YUYV

设置摄像头的采集通道

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

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

struct v4l2_format {
	__u32	 type;//V4L2_BUF_TYPE_VIDEO_CAPTURE
	union {
		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 */
};

//使用
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_YVYU;
format.fmt.pix.field= V4L2_FIELD_NONE ;

ioctl(fd,VIDIOC_S_FMT,&format);
//ioctl(fd,VIDIOC_G_FMT,&format);//获取格式

申请缓存空间,映射到用户空间,缓冲区设计成队列

image-20220826212456705

申请缓存空间

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

//使用
struct v4l2_requestbuffers req = {};
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;

ioctl(fd,VIDIOC_REQBUFS,&req);

将缓冲区分配并映射到用户空间

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_MMAP
	union {
		__u32           offset;//偏移
		unsigned long   userptr;
		struct v4l2_plane *planes;
		__s32		fd;
	} m;
	__u32			length;//长度
	__u32			reserved2;
	__u32			reserved;
};

//使用
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);

申请的缓冲区加入到v4l2的采集队列

ioctl(fd,VIDIOC_QBUF,&buf);

启动摄像头采集

enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd,VIDIOC_STREAMON,&buf_type);

从采集队列中获取摄像头采集的数据

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

关闭摄像头采集

enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd,VIDIOC_STREAMOFF,&buf_type);

//解除映射
//关闭设备文件

(4)V4L2和Qt界面的结合

练习:

1)使用Qt进程运行camera程序 --------- 作业

2)使用Qt线程实现V4L2摄像头的采集

Qt中开启线程采集摄像头数据,采集一帧通过信号将该帧数据传递给界面,在界面中对数据进行显示,处理,识别.....

串口

概念

狭义的串口是一种非常古老,应用非常广泛的通信方式,PC定义了标准串口规范 --------- RS232标准 -------- 9针/25针,常用的串口只保留其中4个脚:VCC GND TXD RXD

广义的串口泛指一切的串行通信方式(一个时间点传输1位数据)

Linux系统串口应用编程

Linux系统提供了一系列的接口函数来访问串口,通过调用这些函数可以实现串口传输

相关的头文件和数据结构

#include <termios.h>

struct termios
{
    tcflag_t c_iflag;		/* 输入模式标志 */
    tcflag_t c_oflag;		/* 输出模式标志 */
    tcflag_t c_cflag;		/* 控制模式标志 */
    tcflag_t c_lflag;		/* 本地模式标志 */
    cc_t c_line;			/* 线路规程 */
    cc_t c_cc[NCCS];		/* 控制属性 */
    speed_t c_ispeed;		/* 输入速度 */
    speed_t c_ospeed;		/* 输出速度 */
#define _HAVE_STRUCT_TERMIOS_C_ISPEED 1
#define _HAVE_STRUCT_TERMIOS_C_OSPEED 1
};

串口操作相关函数

image-20220826212509454

开发板串口的设备文件 ----------- /dev/ttySAC0...1...2...3...

/dev/ttySAC0 ------ 调试串口
/dev/ttySAC1 ------ con2
/dev/ttySAC2 ------ con3
/dev/ttySAC3 ------ con4

Linux串口编程步骤

打开串口设备文件

serial_fd = open("/dev/ttySAC0",O_RDWR|O_NOCTTY);
//O_NOCTTY -------- 打开的串口不作为控制终端

备份串口的配置参数

struct termios old_cfg;
struct termios new_cfg;

tcgetattr(serial_fd, &old_cfg);

配置串口为原始模式

new_cfg = old_cfg;

cfmakeraw(&new_cfg);

配置波特率

cfsetispeed(&new_cfg,B115200);
cfsetospeed(&new_cfg,B115200);

配置控制标志

new_cfg.c_cflag |= CREAD|CLOCAL;//使能数据接收和本地模式

配置帧结构

new_cfg.c_cflag &= ~CSTOPB;//1位停止位
new_cfg.c_cflag &= ~CSIZE;//去掉数据位屏蔽
new_cfg.c_cflag |= CS8;//8位数据位
new_cfg.c_cflag &= ~PARENB;//无校验

设置阻塞模式

tcflush(serial_fd,TCIOFLUSH);

//收到1字节解除阻塞
new_cfg.c_cc[VTIME] = 0;
new_cfg.c_cc[VMIN] = 1;

tcflush(serial_fd,TCIOFLUSH);

将配置好的结构体写入系统

tcsetattr(serial_fd,TCSANOW,&new_cfg);
    TCSANOW - 立即生效
    TCSADRAIN - 输入输出完成后生效
    TCSAFLUSH - 刷新缓冲区后生效

使用read/write发送和接收数据

串口接口的RFID

操作步骤

1.初始化串口
2.请求卡 ---------- 寻找附近有信号的卡
    int PiccRequest(int fd);
3.防碰撞 ---------- 找出一张信号最强(最近)的卡
    int PiccAnticoll(int fd);//得到卡ID
4.选卡 ---------- 选择一张通信的卡
    int PiccSelect(int fd);
5.密钥验证 ------ 验证身份
    int PiccAuthKey(int fd,int sector,unsigned char KeyAB);
6.读写卡
    int PiccRead(int fd,unsigned char sector);
    int PiccWrite(int fd,unsigned char sector);    

RFID模块和开发板的连接

image-20220826212525180

作业:

1.使用Qt进程运行camera程序并实现控制

2.将RFID的程序移植到Qt,并且将读出的RFID显示到界面上