Linux摄像头
摄像头的视频格式
常见摄像头的视频格式有很多种,比如RGB,YUYV,MJPEG......,获取到摄像头的图片流后需要按照格式做不同的处理。
Linux中摄像头的层次框架
V4L2摄像头驱动框架的访问是通过系统IO的接口 ------ ioctl函数,ioctl专用于硬件控制的系统IO的接口。
摄像头的设备文件
Linux中V4L2摄像头对应的设备文件是 /dev/video0...1...2...
插入USB摄像头后
摄像头的设备文件 ----------- /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);
练习:
获取摄像头采集格式,是不是支持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);//获取格式
申请缓存空间,映射到用户空间,缓冲区设计成队列
申请缓存空间
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
};
串口操作相关函数
开发板串口的设备文件 ----------- /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模块和开发板的连接
作业:
1.使用Qt进程运行camera程序并实现控制
2.将RFID的程序移植到Qt,并且将读出的RFID显示到界面上