<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[飞凌 OK153-S 开发板试用:接口获取 USB 摄像头的 YUYV 格式图像数据]]></title><description><![CDATA[<p dir="auto">使用飞凌 OK153-S 开发板 外接一个 usb摄像头，获取摄像头的YUYV数据<br />
<img src="/assets/uploads/files/1769665966851-f7c096c6-02fa-4082-a6c7-3c36e0d6340d-87609187e637d41a75f2ebea64f47ad3-resized.jpg" alt="f7c096c6-02fa-4082-a6c7-3c36e0d6340d-87609187e637d41a75f2ebea64f47ad3.jpg" class=" img-responsive img-markdown" /><br />
插上USB接口，对应的节点，/dev/video1<br />
<img src="/assets/uploads/files/1769666153327-9a058af0-2c01-4184-a91d-4b7ce9a57ced-image.png" alt="9a058af0-2c01-4184-a91d-4b7ce9a57ced-image.png" class=" img-responsive img-markdown" width="689" height="450" /></p>
<p dir="auto">代码如下（v4l2_yuyv_capture.c）:<br />
#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;string.h&gt;<br />
#include &lt;fcntl.h&gt;<br />
#include &lt;unistd.h&gt;<br />
#include &lt;errno.h&gt;<br />
#include &lt;sys/mman.h&gt;<br />
#include &lt;sys/ioctl.h&gt;<br />
#include &lt;linux/videodev2.h&gt;</p>
<p dir="auto">// 配置参数（可根据摄像头支持情况修改）<br />
#define DEVICE_PATH "/dev/video1"  // 摄像头设备节点<br />
#define WIDTH 640                  // 图像宽度<br />
#define HEIGHT 480                 // 图像高度<br />
#define BUFFER_COUNT 4             // 缓冲区数量（推荐2-4个）</p>
<p dir="auto">// 缓冲区结构体，保存映射后的地址和长度<br />
typedef struct {<br />
void *start;<br />
size_t length;<br />
} BufferInfo;</p>
<p dir="auto">BufferInfo *buffers = NULL;  // 缓冲区数组</p>
<p dir="auto">// 错误处理辅助函数<br />
static void err_exit(const char *msg) {<br />
perror(msg);<br />
exit(EXIT_FAILURE);<br />
}</p>
<p dir="auto">// 打开视频设备<br />
static int open_video_device(const char *dev_path) {<br />
int fd = open(dev_path, O_RDWR | O_NONBLOCK, 0);  // O_NONBLOCK：非阻塞模式<br />
if (fd == -1) {<br />
err_exit("Failed to open video device");<br />
}<br />
return fd;<br />
}</p>
<p dir="auto">// 查询设备是否支持视频捕获和YUYV格式<br />
static void check_device_capability(int fd) {<br />
struct v4l2_capability cap;<br />
if (ioctl(fd, VIDIOC_QUERYCAP, &amp;cap) == -1) {<br />
err_exit("Failed to query device capability");<br />
}</p>
<pre><code>// 检查是否支持视频捕获（摄像头属于视频输入设备）
if (!(cap.capabilities &amp; V4L2_CAP_VIDEO_CAPTURE)) {
    fprintf(stderr, "Device does not support video capture\n");
    exit(EXIT_FAILURE);
}

// 检查是否支持内存映射方式（高效获取帧数据）
if (!(cap.capabilities &amp; V4L2_CAP_STREAMING)) {
    fprintf(stderr, "Device does not support streaming I/O\n");
    exit(EXIT_FAILURE);
}
</code></pre>
<p dir="auto">}</p>
<p dir="auto">// 设置视频格式为YUYV<br />
static void set_video_format(int fd) {<br />
struct v4l2_format fmt;<br />
memset(&amp;fmt, 0, sizeof(fmt));<br />
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;<br />
fmt.fmt.pix.width = WIDTH;<br />
fmt.fmt.pix.height = HEIGHT;<br />
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;  // 设置为YUYV格式<br />
fmt.fmt.pix.field = V4L2_FIELD_NONE;  // 无场（逐行扫描）</p>
<pre><code>if (ioctl(fd, VIDIOC_S_FMT, &amp;fmt) == -1) {
    err_exit("Failed to set video format");
}

// 验证实际设置的格式（摄像头可能会调整分辨率）
if (fmt.fmt.pix.width != WIDTH || fmt.fmt.pix.height != HEIGHT) {
    printf("Warning: Device adjusted resolution to %dx%d\n",
           fmt.fmt.pix.width, fmt.fmt.pix.height);
}
if (fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) {
    fprintf(stderr, "Device does not support YUYV format\n");
    exit(EXIT_FAILURE);
}
</code></pre>
<p dir="auto">}</p>
<p dir="auto">// 请求并分配内核缓冲区<br />
static void request_buffers(int fd) {<br />
struct v4l2_requestbuffers req;<br />
memset(&amp;req, 0, sizeof(req));<br />
req.count = BUFFER_COUNT;<br />
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;<br />
req.memory = V4L2_MEMORY_MMAP;  // 使用内存映射方式</p>
<pre><code>if (ioctl(fd, VIDIOC_REQBUFS, &amp;req) == -1) {
    err_exit("Failed to request buffers");
}

if (req.count != BUFFER_COUNT) {
    fprintf(stderr, "Device only allocated %d buffers (requested %d)\n",
            req.count, BUFFER_COUNT);
    exit(EXIT_FAILURE);
}

// 分配缓冲区数组内存
buffers = (BufferInfo *)malloc(req.count * sizeof(BufferInfo));
if (!buffers) {
    err_exit("Failed to allocate buffer info array");
}
</code></pre>
<p dir="auto">}</p>
<p dir="auto">// 将内核缓冲区映射到用户空间<br />
static void mmap_buffers(int fd) {<br />
for (int i = 0; i &lt; BUFFER_COUNT; i++) {<br />
struct v4l2_buffer buf;<br />
memset(&amp;buf, 0, sizeof(buf));<br />
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;<br />
buf.memory = V4L2_MEMORY_MMAP;<br />
buf.index = i;</p>
<pre><code>    // 查询缓冲区信息
    if (ioctl(fd, VIDIOC_QUERYBUF, &amp;buf) == -1) {
        err_exit("Failed to query buffer");
    }

    // 内存映射
    buffers[i].length = buf.length;
    buffers[i].start = mmap(NULL, buf.length,
                            PROT_READ | PROT_WRITE,  // 可读可写
                            MAP_SHARED,              // 共享映射（内核/用户空间共享）
                            fd, buf.m.offset);

    if (buffers[i].start == MAP_FAILED) {
        err_exit("Failed to mmap buffer");
    }

    // 将缓冲区放入输入队列（准备捕获数据）
    if (ioctl(fd, VIDIOC_QBUF, &amp;buf) == -1) {
        err_exit("Failed to queue buffer");
    }
}
</code></pre>
<p dir="auto">}</p>
<p dir="auto">// 启动视频流捕获<br />
static void start_stream(int fd) {<br />
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;<br />
if (ioctl(fd, VIDIOC_STREAMON, &amp;buf_type) == -1) {<br />
err_exit("Failed to start stream");<br />
}<br />
}</p>
<p dir="auto">// 获取一帧YUYV格式数据并保存到文件<br />
static void capture_one_frame(int fd) {<br />
struct v4l2_buffer buf;<br />
fd_set fds;<br />
struct timeval tv;<br />
int ret;</p>
<pre><code>// 等待缓冲区就绪（非阻塞模式下需要轮询或select）
FD_ZERO(&amp;fds);
FD_SET(fd, &amp;fds);
tv.tv_sec = 5;  // 超时时间5秒
tv.tv_usec = 0;

ret = select(fd + 1, &amp;fds, NULL, NULL, &amp;tv);
if (ret == -1) {
    err_exit("Failed to select");
} else if (ret == 0) {
    fprintf(stderr, "Select timeout (no frame captured)\n");
    exit(EXIT_FAILURE);
}

// 从输出队列取出就绪的缓冲区
memset(&amp;buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;

if (ioctl(fd, VIDIOC_DQBUF, &amp;buf) == -1) {
    // 忽略临时的EAGAIN错误（缓冲区尚未就绪）
    if (errno != EAGAIN) {
        err_exit("Failed to dequeue buffer");
    }
    return;
}

// 验证帧数据大小（YUYV格式应为 宽×高×2）
printf("Captured frame: index=%d, length=%zu, expected=%d\n",
       buf.index, buf.bytesused, WIDTH * HEIGHT * 2);

// 将YUYV数据保存到文件（后缀可设为.yuv，方便后续工具查看）
FILE *fp = fopen("capture_yuyv.yuv", "wb");
if (!fp) {
    err_exit("Failed to open output file");
}
fwrite(buffers[buf.index].start, 1, buf.bytesused, fp);
fclose(fp);
printf("YUYV frame saved to capture_yuyv.yuv\n");

// 将缓冲区重新放入输入队列，继续捕获后续帧（如需连续捕获）
if (ioctl(fd, VIDIOC_QBUF, &amp;buf) == -1) {
    err_exit("Failed to requeue buffer");
}
</code></pre>
<p dir="auto">}</p>
<p dir="auto">// 停止视频流捕获<br />
static void stop_stream(int fd) {<br />
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;<br />
if (ioctl(fd, VIDIOC_STREAMOFF, &amp;buf_type) == -1) {<br />
err_exit("Failed to stop stream");<br />
}<br />
}</p>
<p dir="auto">// 解除内存映射并释放资源<br />
static void cleanup_resources(int fd) {<br />
// 解除映射<br />
for (int i = 0; i &lt; BUFFER_COUNT; i++) {<br />
if (munmap(buffers[i].start, buffers[i].length) == -1) {<br />
err_exit("Failed to unmap buffer");<br />
}<br />
}</p>
<pre><code>// 释放缓冲区数组
free(buffers);
buffers = NULL;

// 关闭设备
close(fd);
</code></pre>
<p dir="auto">}</p>
<p dir="auto">int main() {<br />
int fd = -1;</p>
<pre><code>// 执行完整捕获流程
fd = open_video_device(DEVICE_PATH);
check_device_capability(fd);
set_video_format(fd);
request_buffers(fd);
mmap_buffers(fd);
start_stream(fd);
capture_one_frame(fd);
stop_stream(fd);
cleanup_resources(fd);

return EXIT_SUCCESS;
</code></pre>
<h1>}</h1>
<p dir="auto">虚拟机中编译：/opt/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/arm-linux-gnueabihf-gcc  v4l2_yuyv_capture.c -o v4l2_yuyv_capture</p>
<h1><img src="/assets/uploads/files/1769666331794-5d0af425-6d61-4b0f-b8d4-dbcbd7b2d289-image.png" alt="5d0af425-6d61-4b0f-b8d4-dbcbd7b2d289-image.png" class=" img-responsive img-markdown" width="1391" height="438" /></h1>
<p dir="auto">拷贝v4l2_yuyv_capture到OK153上运行<br />
<img src="/assets/uploads/files/1769667280148-80a4e609-4108-46cd-81a6-a94e5c45bb65-17a6f4145fc16ed8c43ae33397f51061.png" alt="80a4e609-4108-46cd-81a6-a94e5c45bb65-17a6f4145fc16ed8c43ae33397f51061.png" class=" img-responsive img-markdown" width="667" height="425" /><br />
然后用YUYV软件查看获取的yuyv数据帧<br />
<img src="/assets/uploads/files/1769667364811-da6f89b4-5281-4ba7-b30e-54dde1ef06c6-image.png" alt="da6f89b4-5281-4ba7-b30e-54dde1ef06c6-image.png" class=" img-responsive img-markdown" width="651" height="559" /><br />
这个摄像头镜头有点花，手头没有别的摄像头了，勉强可以使用，<br />
OK，搞定了</p>
]]></description><link>https://bbs.aw-ol.com/topic/6881/飞凌-ok153-s-开发板试用-接口获取-usb-摄像头的-yuyv-格式图像数据</link><generator>RSS for Node</generator><lastBuildDate>Fri, 13 Mar 2026 10:55:11 GMT</lastBuildDate><atom:link href="https://bbs.aw-ol.com/topic/6881.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 29 Jan 2026 06:17:29 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to 飞凌 OK153-S 开发板试用:接口获取 USB 摄像头的 YUYV 格式图像数据 on Thu, 29 Jan 2026 09:21:00 GMT]]></title><description><![CDATA[<p dir="auto">感谢楼主的分享，很有用</p>
]]></description><link>https://bbs.aw-ol.com/post/28110</link><guid isPermaLink="true">https://bbs.aw-ol.com/post/28110</guid><dc:creator><![CDATA[kzzidd]]></dc:creator><pubDate>Thu, 29 Jan 2026 09:21:00 GMT</pubDate></item></channel></rss>