IP摄像头技术纵览(一)— linux 内核编译,USB摄像头设备识别
开始正文之前先来认识一下我的开发环境:
系统:ubuntu 10.04
开发板:AT91SAM9260 + Linux-2.6.30
USB摄像头:UVC无驱摄像头(着手开发时只是随便买了个usb摄像头,根本不知道摄像头还有那么多讲究)
关于UVC摄像头,这里引用度娘的一段解释:
UVC,全称为:USB video class 或USB video device class,UVC是Microsoft与另外几家设备厂商联合推出的为USB视频捕获设备定义的协议标准,目前已成为USB org标准之一。如今的主流操作系统(如Windows XP SP2 and later, Linux 2.4.6 and later, MacOS 10.5 and later)都已提供UVC设备驱动,因此符合UVC规格的硬件设备在不需要安装任何的驱动程序下即可在主机中正常使用。
使用 UVC 的好处是省略了驱动程序安装这一环节,所以称为无驱—-其实就是不用安装驱动的意思。
开始正文前先来看一下我的开发板:
有没有很霸气 (0^◇^0)/,IP64防护等级,绝对秒杀其他开发板,不服拍倒:-D。好了,闲话少提,进入正题。
本文属于《IP摄像头技术纵览》系列文章之一:
Author: chad
Mail: linczone@163.com
本文可以自由转载,但转载请务必注明出处以及本声明信息。
引子
天地初开,大道始行。进行Linux开发的第一步是创建交叉编译工具链,不同的Linux内核版本,不同的硬件平台,我们使用的交叉工具链是不同的。曾经,很长一段时间我一直有个疑惑:为什么我用at91sam9260交叉编译工具链编译的程序只能在at91sam9260上运行,在mini2440上就不能运行?相反,用使用于mini2440的交叉编译工具链编译的程序在at91上也不能运行?mini2440与at91sam9260都是arm平台,同样使用linux系统,为何二进制程序不能通用呢?
交叉编译工具链的创建只是第一步,为了实现usb摄像头图像采集功能,又该如何配着linux内核,如何检测usb摄像头并采集图像呢?
要搞清楚这些问题,本文将追本溯源,从头到尾,围绕以下问题展开:
- 交叉编译工具链是什么?
- 交叉编译工具链有什么组成?
- 交叉编译工具链如何工作?
- 如何自己创建交叉编译工具链?
- 如何配置、编译Linux内核?
- 如何创建Linux文件系统?
- 如何编写USB摄像头视频图像采集程序?
- 编译完成的程序文件是什么格式?
- 在我们运行程序文件时,程序是如何运行起来的?
- 传说中的linux虚拟存储管理是什么?它与程序的运行有什么关系?
。。。。。。。
问题是无穷的,只有有问题的程序员才能越走越远。我们是不建议重复发明轮子的,但如果我们不自己发明一次,我们永远不知道轮子是怎么来的。我在这里只发问,引起大家的思考,然后给出部分答案(我也没能力给出太多^_^o~ 努力!)。我相信,只有明白了大道,才能更好的开发。
一、内核编译、文件系统移植—茅庐初创
上文已说,内核编译的第一步是建立交叉编译工具,那么:
1. 交叉编译工具链是什么?
交叉编译是嵌入式开发过程中的一项重要技术,其主要特征是某机器中执行的程序代码不是在本机编译生成,而是由另一台机器编译生成,一般把前者称为目标机,后者称为主机。
采用交叉编译的主要原因在于,多数嵌入式目标系统不能提供足够的资源供编译过程使用,因而只好将编译工程转移到高性能的主机中进行,这就需要在强大的pc机上建立一个用于目标机的交叉编译环境。
2. 交叉编译工具链有什么组成?
交叉编译工具链是一个由编译器、连接器和解释器组成的综合开发环境。
Linux下的交叉编译环境重要包括以下几个部分:
(1)针对目标系统的编译器gcc/g++;
(2)针对目标系统的二进制工具binutils;
(3)目标系统的标准c库glibc,有时出于减小libc 库大小的考虑,你也可以用别的c库来代替glibc,例如uClibc、newlib等;
(4)目标系统的Linux内核头文件等。
3. 交叉编译工具链如何工作—编译原理?
使用gcc编译程序时,编译过程可被细分为四个阶段:
(1)预处理
(2)编译
(3)汇编
(4)链接
以hello.c为例:
#include <stdio.h>
int main()
{
printf("hello world.\n");
return 0;
}
1、预处理(Preprocessing)
预处理阶段,编译器将对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析,它把”stdio.h”的内容插入到hello.i文件中,用户使用-E选项进行查看:
gcc -E hello.c -o hello.i
2、编译(Compilation)
gcc首先检查语法的规范性以及是否有语法错误等,以确定代码实际要做的工作,用户可以使用”-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码:
gcc -S hello.i -o hello.s
3、汇编(Assembly)
汇编阶段是把编译阶段生成的“.s”文件转成目标文件,用户在此可使用选项”-c”就可看到汇编代码已转化为”.o”的二进制目标代码:
gcc -c hello.s -o hello.o
4、链接(Linking)
在该阶段涉及一个重要的概念:函数库。上例程序中并没有定义”printf”的函数实现,在预编译中包含进的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,”printf”函数是如何被调用的呢?最后的答案是:系统把这些函数实现都己经被放入名为libc.so.6的库文件中去了,在没有特别指定时库函数搜索路径时,gcc会到系统默认的搜索路径“/usr/Iib”下进行查找,链接到libc.so.6库中的”printf”实现,链接的最终结果是生成可执行ELF文件:
gcc hello.o –o hello
4. 如何自己创建交叉编译工具链?
进行嵌入式Linux开发的第一步是创建交叉编译工具链,在过去很长的一段时间里,构建一套交叉编译工具链对于嵌入式开发者来说简直是一场恶梦,因为他们得手动跟踪各种源码包(及其更新包)之间的依赖关系。直到buildroot的出现改变了这一事实。
Buildroot是一个Makefiles和patches的命令集,它可以非常简单的为你的目标系统产生一个交叉编译工具链和根文件系统,整个创建过程就如同编译Linux内核一般。
在Linux中使用Buildroot建立整个ARM交叉编译环境的整体过程为:
(1)下载buildroot
(2)安装依赖库软件包
(3)解压buildroot压缩包
(4)进入源码目录,执行make menuconfig配置
(5)保存退出生成.config 文件
(6)编译
(7)修改环境变量
(8)测试arm-linux-gcc
(9)hello.c测试
其中,导致不同平台交叉编译工具链不可通用的主要参数是:
#下面的注释同时对比mini2440平台与at91sam9260平台
Target Architecture(arm) ---> 目标的架构,s3c2440 与 at9260 都是arm,这个相同
Target Architecture Variant(arm926t) ---> 内核类型(s3c2440[arm920t] 而 at91sam9260[arm926EJ-S],但是配置时选择arm926t),此处不同
Target ABI (OABI) ---> 目标使用的应用程序二进制接口,此处不同
①EABI(Embedded ABI) mini2440的选择。
②OABI(Old ABI) at91sam9260的选择
> Kernel Headers (Linux 3.18.x kernel headers) ---> 此处差别影响不大
C library (glibc) ---> 都选择的是glibc
glibc version (2.20) ---> 版本不一样
也正是上面的配置,决定了我们最终生成的交叉工具链是只能针对特定的处理器和操作系统平台的。
编译过程的其他步骤不再说明,感兴趣的可以参考我的另一篇博文《使用buildroot创建自己的交叉编译工具链》。
5. 如何配置、编译Linux内核?
搞明白了交叉编译工具链以后,我们就该进入Linux内核配置阶段了,如为了支持USB无驱摄像头(UVC),我们需要进行的配置如下:
Device Drivers --->
<*> Multimedia support --->
<*> Video For Linux
[*] Enable Video For Linux API 1 (DEPRECATED)
[*] Video capture adapters --->
[*] V4L USB devices --->
<*> USB Video Class (UVC)
[*] UVC input events device support
[M] GSPCA based webcams --->
Linux内核中已经集成了几乎你能找到的所有的摄像头的驱动程序,添加摄像头支持时可以根据情况在编译内核的时候进行针对性配置,或者索性把摄像头驱动相关的全部编译在内核中(这样势必造成内核尺寸增大,一般linux系统移植时已经设定好了Flash分区大小,所以,如果内核尺寸超过限值,将导致系统启动异常),这样你就可以宣称你的开发板支持所有的摄像头了(^o^)/。其中的GSPCA 是一个法国程序员在业余时间制作的一个万能USB 摄像头驱动程序,我没有用到,所以仅作为演示模块编译,大家实际使用时候根据自己的摄像头情况进行选择。
配置好Linux内核以后,进行编译:
make ARCH=arm CROSS_COMPILE=arm-linux- uImage
如果要编译生成GSPCA内核模块,则使用如下命令:
make modules
关于Linux编译后生成镜像格式的简要说明如下:
zImage是ARM Linux常用的一种压缩映像文件,uImage是U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的“头”,说明这个映像文件的类型、加载位置、生成时间、大小等信息。换句话说,如果直接从uImage的0x40位置开始执行,zImage和uImage没有任何区别。另外,Linux2.4内核不支持uImage,Linux2.6内核加入了很多对嵌入式系统的支持,但是uImage的生成也需要设置。
格式 | 说明 |
---|---|
vmlinux | 编译出来的最原始的内核文件,未压缩。 |
zImage | 是vmlinux经过gzip压缩后的文件。 |
bzImage | bz表示“big zImage”,不是用bzip2压缩的。两者的不同之处在于,zImage解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么采用zImage或bzImage都行,如果比较大应该用bzImage。 |
uImage | U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的tag。 |
vmlinuz | 是bzImage/zImage文件的拷贝或指向bzImage/zImage的链接。 |
initrd | 是“initial ramdisk”的简写。一般被用来临时的引导硬件到实际内核vmlinuz能够接管并继续引导的状态。 |
我开发板的引导程序是uboot,所以这里生成的是uImage 。
6.创建Linux文件系统
根文件系统使用 busybox 制作,busybox 以小巧著称,适合于嵌入式设备的linux 文件系统。具体制作及移植过程参考《at91sam9260 Linux 系统文件系统定制》一文。
二、USB摄像头初识
Linux UVC driver(uvc) 该驱动适用于符合USB视频类(USB Video Class)规范的摄像头设备,它包括V4L2内核设备驱动和用户空间工具补丁。大多数大容量存储器设备(如优盘)都遵循USB规范,因而仅用一个单一驱动就可以操作它们。与此类似,UVC兼容外设只需要一个通用驱动即可。
USB摄像头大体上可以分为UVC cameras和non-UVC cameras。推荐购买UVC cameras。UVC是一个开放的标准,拥有维护良好的驱动,它属于内核代码的一部分。non- UVC cameras通常情况下不比UVC cameras