第一讲:课程导学与环境搭建

各位同学,大家好。我是你们这门课的老司机。说实话,每次开新课前,我最怕的就是环境搭建这一趴。为啥?因为太多人卡在这一步,后面全白搭。今天咱们就把这事儿捋清楚。

嵌入式Linux摄像头开发,说白了就是让一块开发板能接上摄像头,把图像数据抓出来,再拿去处理或显示。听起来简单,但坑不少。我当年第一次调摄像头驱动,折腾了整整三天,最后发现是电源没给够——嗯,这种糗事后面慢慢跟你们聊。

1.1 开发板选型:别盲目跟风

选开发板这事儿,我个人的建议是:看需求,别只看参数

市面上常见的板子,我列个表给你们参考:

开发板 处理器 摄像头接口 适合场景
树莓派4B BCM2711 CSI-2 入门学习、原型验证
i.MX6ULL Cortex-A7 并行DVP 工业应用、低功耗
RK3399 Cortex-A72+A53 MIPI CSI AI视觉、高清视频
全志V3s Cortex-A7 DVP/MIPI 低成本方案

我个人习惯用树莓派做教学演示,因为社区资源多,遇到问题好查。但如果你要做产品,i.MX6ULL或RK3399更靠谱。我在项目中遇到过用树莓派做产品,结果供货不稳定,差点翻车。

我的建议:初学者先别纠结板子,手头有什么就用什么。V4L2框架是通用的,换板子只是改改设备树和驱动配置。

1.2 虚拟机与交叉编译链安装

好,板子选好了,接下来是开发环境。你想想看,在PC上写代码,编译完要跑到ARM板子上跑,这就得用交叉编译链

我推荐用Ubuntu 20.04 LTS作为宿主机系统。为啥?因为大部分芯片厂商的SDK都是基于这个版本测试的。你非要用最新的Ubuntu 24.04,也行,但可能会遇到一些库版本不兼容的问题——我踩过这个坑。

安装步骤(以树莓派为例)

  1. 安装虚拟机:VMware Workstation或VirtualBox都行。我个人偏爱VMware,稳定。
  2. 安装Ubuntu:下载镜像,一路下一步。记得给虚拟机分配至少4GB内存,40GB硬盘。
  3. 安装交叉编译链
# 下载树莓派官方交叉编译工具
git clone https://github.com/raspberrypi/tools.git
# 设置环境变量
export PATH=$PATH:~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
# 验证安装
arm-linux-gnueabihf-gcc --version

这里要注意:交叉编译链的版本一定要和板子上的系统匹配。我曾经用了一个太新的gcc版本,编译出来的程序在板子上跑不起来,报了一堆段错误。后来发现是glibc版本不兼容。

避坑指南:不要用sudo pip或sudo apt随便装交叉编译链。最好从芯片厂商官网下载官方提供的工具链。我见过太多人因为工具链版本不对,浪费了一整天。

1.3 V4L2框架初探

V4L2,全称Video for Linux 2。说白了,它就是Linux内核里给视频设备提供的一套标准接口。你写应用程序时,不用管底层是USB摄像头还是CSI摄像头,统一用open、ioctl、mmap这些系统调用就行。

我刚开始学V4L2时,觉得它好复杂。后来发现,其实核心就几个步骤:

  1. 打开设备:open("/dev/video0")
  2. 查询能力:ioctl(VIDIOC_QUERYCAP)
  3. 设置格式:ioctl(VIDIOC_S_FMT)
  4. 申请缓冲区:ioctl(VIDIOC_REQBUFS)
  5. 开始采集:ioctl(VIDIOC_STREAMON)
  6. 读取数据:ioctl(VIDIOC_DQBUF) + mmap

嗯,是不是感觉有点像流水线?没错,V4L2就是这么设计的。

核心概念:V4L2把摄像头抽象成一个文件。你读写这个文件,就是在和摄像头交互。这种设计思想在Linux里随处可见——一切皆文件。

一个最简单的V4L2示例

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>

int main() {
    int fd = open("/dev/video0", O_RDWR);
    if (fd < 0) {
        perror("open");
        return -1;
    }

    struct v4l2_capability cap;
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
        perror("VIDIOC_QUERYCAP");
        close(fd);
        return -1;
    }

    printf("驱动: %s\n", cap.driver);
    printf("设备: %s\n", cap.card);
    printf("总线: %s\n", cap.bus_info);

    close(fd);
    return 0;
}

这段代码很简单,就是打开摄像头设备,查询它的能力,然后打印出来。你编译运行后,应该能看到类似这样的输出:

驱动: uvcvideo
设备: USB Camera
总线: usb-0000:00:14.0-1

如果报错"Permission denied",记得加sudo。或者把你的用户加到video组里:

sudo usermod -a -G video $USER

然后重新登录一下。

调试小技巧:用v4l2-ctl工具可以快速查看摄像头信息,不用写代码:
v4l2-ctl --list-devices
v4l2-ctl -d /dev/video0 --all
这个工具在v4l-utils包里,sudo apt install v4l-utils就能装。

1.4 本章小结

好,咱们第一讲就到这儿。你想想看,今天咱们干了啥?

  • 选了开发板——别盲目,看需求
  • 搭了环境——虚拟机+交叉编译链,版本要匹配
  • 初探了V4L2——其实就是open、ioctl、mmap那套

下一讲,咱们要深入V4L2的核心——缓冲区管理。到时候我会手把手教你写一个完整的摄像头采集程序。嗯,那个程序我当年写了五版才跑通,你们可以少走很多弯路。

课后作业:在你的板子上运行上面的示例代码,把摄像头信息打印出来。如果遇到问题,先自己查查,实在搞不定,咱们下节课讨论。

公众号:蓝海资料掘金营,微信deep3321