前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >全志D1-H开发板USB摄像头拍照Demo

全志D1-H开发板USB摄像头拍照Demo

作者头像
阿志小管家
发布2024-03-05 10:00:08
1950
发布2024-03-05 10:00:08
举报
文章被收录于专栏:全志嵌入式那些事

USB摄像头拍照Demo

本章节将讲解如何D1-H上使用一个USB摄像头拍摄一张照片。

D1-H哪吒开发板上有一个USB Host接口(即电脑上那种插鼠标键盘的USB口),同时D1-H Tina Linux支持UVC(USB Video Class,USB视频类),这样D1-H就具备了开发和使用USB摄像头的软硬件条件。

前期准备

硬件准备
  • USB免驱摄像头一个,标准USB摄像头均可,淘宝直接搜“USB摄像头”搜出来排名靠前的随便买一个就行,本文中调试用到的是一个海康威视的摄像头,零售价格大概数十元。
  • 哪吒开发板一块
软件准备
  • 在运行本例之前,请确保你的Hello Word 的过程没有出现问题。即交叉编译工具和ADB工具都可正常使用或已经添加进环境变量。
  • 下文将详细介绍demo的源码写的内容。

硬件连接

主要连接串口调试,USB连接电脑可以用来传输数据和供电,USB摄像头连接到开发板的USB接口。

连接好的系统就是下图的样子:

如果此时你的开发板是开机的话,终端会打印USB摄像头连接的Log。如下图:

从图中可以看到,摄像头为 HIK 720P 的摄像头,同时摄像头挂在 USB1总线、为 input3 设备。

此时我们查看/dev 外设目录,可以发现有/dev/video0 /dev/video1设备,video1 为 video0的映射。

到此,我们的哪吒开发板已经成功连接上了USB摄像头,下一步是写程序来使用它。

程序获取

在编写程序之前,我们需要了解一下Linux中摄像头的接口标准。在LInux系统中,摄像头之所以能被识别离不开我们的系统对摄像头的驱动支持。

Video4Linux2(Video for Linux Two, 简称V4L2)是Linux中关于视频设备的驱动框架,为上层访问底层的视频设备提供统一接口。V4L2主要支持三类设备:视频输入输出设备、VBI设备和Radio设备,分别会在/dev目录下产生videoX、vbiX和radioX设备节点,其中X是0,1,2等的数字。如USB摄像头是我们常见的视频输入设备。

Linux 中强大的第三方库如:FFmpeg和OpenCV对V4L2均支持。

本例就使用V4L2库完成摄像头对图片的捕捉,并将其保存为一张图片。

依照Tina SKD开发架构,我们的代码创建在prckage目录下,我们新建文件夹camerademo在文件夹中新建test.c,或将提供资源中的代码包中的test.c拷贝至此处。

代码语言:javascript
复制
cd d1-tina-open/package/test
mkdir camerademo
cd camerdemo
touch test.c

程序编译

程序编译采用最简单的命令行编译程序:

代码语言:javascript
复制
riscv64-unknown-linux-gnu-gcc test.c -o test

编译后,我们当前目录就会生成可执行文件test

文件烧录及传输

我们在windows中使用ADB工具将其送入开发板中:

代码语言:javascript
复制
adb push test ./.

程序运行

文件传输成功后,我们从开发板中运行。

运行之前首先要赋予文件可执行权限,然后再运行。

代码语言:javascript
复制
chmod +x test
./test

运行截图:

此时程序会打印保存成功的Log。我们使用Ctrl+C终止程序运行后,可在当前文件夹看到有1.jpeg图片生成,我们将他拿出来查看。

依然使用ADB

代码语言:javascript
复制
adb pull /root/1.jpeg .

运行截图:

打开1.jpeg可以看到刚才拍摄到的图片:

如果可以成果看到拍摄的图片,那么恭喜你,你已经给D1-H哪吒添上了一双眼睛,以后你就可以用这双眼睛,去探索这个有趣的世界了!

进阶:程序代码注释及讲解

开头说过我们Linux使用的是V4L2框架获取的摄像头数据。该框架的使用流程如下:

  1. 打开设备
  2. 初始化设备
  3. 注册内存映射I/O
  4. 开始捕捉
  5. 停止捕捉
  6. 关闭设备

根据如上大纲,我们分布讲解。

1. 打开设备

打开设备使用C标准接口open函数,返回文件(设备)描述符。打开文件的方式可以选择**可读可写方式(O_RDWR)无阻塞方式(O_NONBLOCK)**打开

代码语言:javascript
复制
#define CAM_DEV "/dev/video0"
int cam_fd;
if((cam_fd = open(CAM_DEV,O_RDWR)) == -1)
{
    perror("ERROR opening V4L interface.");
    return -1;
}
2. 初始化设备

初始化设备调用init_device函数。

(1) 调用ioctl函数,对设备的I/O通道进行管理,查询设备能力,并将结果保存在结构体v4l2_capability中,结构体v4l2_capability内容如下:

代码语言:javascript
复制
struct v4l2_capability {
	__u8	driver[16]; // name of the driver module (e.g. "bttv")
	__u8	card[32]; // name of the card (e.g. "Hauppauge WinTV")
	__u8	bus_info[32]; // name of the bus (e.g. "PCI:" + pci_name(pci_dev) )
	__u32   version; // KERNEL_VERSION
	__u32	capabilities; // capabilities of the physical device as a whole
	__u32	device_caps; // capabilities accessed via this particular device (node)
	__u32	reserved[3]; // reserved fields for future extensions
};
代码语言:javascript
复制
if(ioctl(cam_fd,VIDIOC_QUERYCAP,&cam_cap) == -1)
    {
        perror("Error opening device %s: unable to query device.");
        return -1;
    }

(2) 判断是否视频捕获设备V4L2_CAP_VIDEO_CAPTURE

代码语言:javascript
复制
if((cam_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) 
    {
        perror("ERROR video capture not supported.");
        return -1;
    }

(3) 调用ioctl函数,设置流数据格式,包括宽、高、像素格式,并将结果保存在结构体v4l2_format中,结构体v4l2_format

代码语言:javascript
复制
struct v4l2_pix_format {
	__u32         	width;
	__u32			height;
	__u32			pixelformat;
	__u32			field;		/* enum v4l2_field */
	__u32           bytesperline;	/* for padding, zero if unused */
	__u32          	sizeimage;
	__u32			colorspace;	/* enum v4l2_colorspace */
	__u32			priv;		/* private data, depends on pixelformat */
};
 
struct v4l2_format { // stream data format
	__u32	 type; // enum v4l2_buf_type; type of the data stream
	union {
		struct v4l2_pix_format		pix;     // V4L2_BUF_TYPE_VIDEO_CAPTURE, definition of an image format
		struct v4l2_pix_format_mplane	pix_mp;  // V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, definition of a multiplanar image format
		struct v4l2_window		win;     // V4L2_BUF_TYPE_VIDEO_OVERLAY, definition of an overlaid image
		struct v4l2_vbi_format		vbi;     // V4L2_BUF_TYPE_VBI_CAPTURE, raw VBI capture or output parameters
		struct v4l2_sliced_vbi_format	sliced;  // V4L2_BUF_TYPE_SLICED_VBI_CAPTURE, sliced VBI capture or output parameters
		__u8	raw_data[200];                   // user-defined, placeholder for future extensions and custom formats
	} fmt;
};

主要设置长宽及出输出格式。

代码语言:javascript
复制
    struct v4l2_format v4l2_fmt;
    v4l2_fmt.type = V4L2_CAP_VIDEO_CAPTURE;
    v4l2_fmt.fmt.pix.width = WIDTH;
    v4l2_fmt.fmt.pix.height = HEIGHT;
    v4l2_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    if (ioctl (cam_fd, VIDIOC_S_FMT, &v4l2_fmt) == -1) 
    {   
        perror("ERROR camera VIDIOC_S_FMT Failed.");
        return -1;
    }
3. 注册内存映射I/O

因为io采用的是MMAP即内存映射方式,因此调用init_mmap函数:

(1) 调用ioctl函数,设置内存映射I/O,并将结果保存在结构体v4l2_requestbuffers中,结构体v4l2_requestbuffers内容如下:

代码语言:javascript
复制
enum v4l2_memory {
	V4L2_MEMORY_MMAP             = 1,
	V4L2_MEMORY_USERPTR          = 2,
	V4L2_MEMORY_OVERLAY          = 3,
	V4L2_MEMORY_DMABUF           = 4,
};
 
struct v4l2_requestbuffers {
	__u32			count;
	__u32			type;		/* enum v4l2_buf_type */
	__u32			memory;		/* enum v4l2_memory */
	__u32			reserved[2];
};
代码语言:javascript
复制
struct v4l2_requestbuffers v4l2_req;
    v4l2_req.count = NB_BUFFER;
    v4l2_req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    v4l2_req.memory = V4L2_MEMORY_MMAP;
    if (ioctl (cam_fd, VIDIOC_REQBUFS, &v4l2_req) == -1) 
    {
        perror("ERROR camera VIDIOC_REQBUFS Failed.");
        return -1;
    } 

(2) 调用ioctl函数,查询缓冲区状态,并将结果保存在结构体v4l2_buffer中.

(3) 调用mmap函数,应用程序通过内存映射将帧缓冲区地址映射到用户空间;通常在需要对文件进行频繁读写时使用,这样用内存读写取代I/O读写,以获得较高的性能。

代码语言:javascript
复制
struct v4l2_buffer v4l2_buf;
    v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    v4l2_buf.memory = V4L2_MEMORY_MMAP;
      for(i = 0; i < NB_BUFFER; i++) 
   {
        v4l2_buf.index = i;
        if(ioctl(cam_fd, VIDIOC_QUERYBUF, &v4l2_buf) < 0)
        {
            perror("Unable to query buffer.");
            return -1;
        }
 
        pic.tmpbuffer[i] = (unsigned char*)mmap(NULL, v4l2_buf.length, PROT_READ, MAP_SHARED, cam_fd, v4l2_buf.m.offset);
        if(pic.tmpbuffer[i] == MAP_FAILED)
        {
             perror("Unable to map buffer.");
             return -1;
        }
        if(ioctl(cam_fd, VIDIOC_QBUF, &v4l2_buf) < 0)
        {
            perror("Unable to queue buffer.");
            return -1;
        }
   }
4. 开始捕捉

调用ioctl函数,VIDIOC_QBUF,并将结果保存在结构体v4l2_buffer中;

代码语言:javascript
复制
struct v4l2_buffer buff;
    buff.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buff.memory = V4L2_MEMORY_MMAP;
    if(ioctl(cam_fd, VIDIOC_DQBUF, &buff) < 0)
    {
        printf("camera VIDIOC_DQBUF Failed.\n");
        usleep(1000*1000);
        return -1;
    }
5. 停止捕捉

调用munmap函数,取消映射设备内存;

代码语言:javascript
复制
 for(i=0; i<NB_BUFFER; i++)
  munmap(pic[0].tmpbuffer[i],pic[0].tmpbytesused[i]);
6. 关闭设备

调用close函数关闭设备。

代码语言:javascript
复制
close(cam_fd);
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • USB摄像头拍照Demo
    • 前期准备
      • 硬件准备
      • 软件准备
    • 硬件连接
      • 程序获取
        • 程序编译
          • 文件烧录及传输
            • 程序运行
              • 进阶:程序代码注释及讲解
                • 1. 打开设备
                • 2. 初始化设备
                • 3. 注册内存映射I/O
                • 4. 开始捕捉
                • 5. 停止捕捉
                • 6. 关闭设备
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档